// Copyright 2021, Collabora, Ltd. // SPDX-License-Identifier: BSL-1.0 /*! * @file * @brief HistoryBuffer collection tests. * @author Ryan Pavlik */ #include #include #include #include using xrt::auxiliary::util::HistoryBuffer; template static inline std::ostream & operator<<(std::ostream &os, const xrt::auxiliary::util::RandomAccessIteratorBase &iter_base) { os << "Iterator@[" << iter_base.index() << "]"; return os; } #include "catch/catch.hpp" TEST_CASE("m_relation_history") { m_relation_history *rh = nullptr; m_relation_history_create(&rh); SECTION("empty buffer") { xrt_space_relation out_relation = XRT_SPACE_RELATION_ZERO; CHECK(m_relation_history_get(rh, 0, &out_relation) == M_RELATION_HISTORY_RESULT_INVALID); CHECK(m_relation_history_get(rh, 1, &out_relation) == M_RELATION_HISTORY_RESULT_INVALID); } SECTION("populated buffer") { xrt_space_relation relation = XRT_SPACE_RELATION_ZERO; relation.relation_flags = (xrt_space_relation_flags)( // XRT_SPACE_RELATION_POSITION_TRACKED_BIT | // XRT_SPACE_RELATION_POSITION_VALID_BIT | // XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT | // XRT_SPACE_RELATION_ORIENTATION_VALID_BIT | // XRT_SPACE_RELATION_LINEAR_VELOCITY_VALID_BIT); // relation.linear_velocity.x = 1.f; // arbitrary value constexpr auto T0 = 20 * (uint64_t)U_TIME_1S_IN_NS; // one second after T0 constexpr auto T1 = T0 + (uint64_t)U_TIME_1S_IN_NS; // two seconds after T0 constexpr auto T2 = T1 + (uint64_t)U_TIME_1S_IN_NS; xrt_space_relation out_relation = XRT_SPACE_RELATION_ZERO; uint64_t out_time = 0; CHECK(m_relation_history_get_size(rh) == 0); CHECK_FALSE(m_relation_history_get_latest(rh, &out_time, &out_relation)); CHECK(m_relation_history_push(rh, &relation, T0)); CHECK(m_relation_history_get_size(rh) == 1); CHECK(m_relation_history_get_latest(rh, &out_time, &out_relation)); CHECK(out_time == T0); relation.pose.position.x = 1.f; CHECK(m_relation_history_push(rh, &relation, T1)); CHECK(m_relation_history_get_size(rh) == 2); CHECK(m_relation_history_get_latest(rh, &out_time, &out_relation)); CHECK(out_time == T1); relation.pose.position.x = 2.f; CHECK(m_relation_history_push(rh, &relation, T2)); CHECK(m_relation_history_get_size(rh) == 3); CHECK(m_relation_history_get_latest(rh, &out_time, &out_relation)); CHECK(out_time == T2); // Try going back in time: should fail to push, leave state the same CHECK_FALSE(m_relation_history_push(rh, &relation, T1)); CHECK(m_relation_history_get_size(rh) == 3); CHECK(m_relation_history_get_latest(rh, &out_time, &out_relation)); CHECK(out_time == T2); CHECK(m_relation_history_get(rh, 0, &out_relation) == M_RELATION_HISTORY_RESULT_INVALID); CHECK(m_relation_history_get(rh, T0, &out_relation) == M_RELATION_HISTORY_RESULT_EXACT); CHECK(out_relation.pose.position.x == 0.f); CHECK(m_relation_history_get(rh, T1, &out_relation) == M_RELATION_HISTORY_RESULT_EXACT); CHECK(out_relation.pose.position.x == 1.f); CHECK(m_relation_history_get(rh, T2, &out_relation) == M_RELATION_HISTORY_RESULT_EXACT); CHECK(out_relation.pose.position.x == 2.f); CHECK(m_relation_history_get(rh, T0 - (uint64_t)U_TIME_1S_IN_NS, &out_relation) == M_RELATION_HISTORY_RESULT_REVERSE_PREDICTED); CHECK(out_relation.pose.position.x < 0.f); CHECK(m_relation_history_get(rh, (T0 + T1) / 2, &out_relation) == M_RELATION_HISTORY_RESULT_INTERPOLATED); CHECK(out_relation.pose.position.x > 0.f); CHECK(out_relation.pose.position.x < 1.f); CHECK(m_relation_history_get(rh, (T1 + T2) / 2, &out_relation) == M_RELATION_HISTORY_RESULT_INTERPOLATED); CHECK(out_relation.pose.position.x > 1.f); CHECK(out_relation.pose.position.x < 2.f); CHECK(m_relation_history_get(rh, T2 + (uint64_t)U_TIME_1S_IN_NS, &out_relation) == M_RELATION_HISTORY_RESULT_PREDICTED); CHECK(out_relation.pose.position.x > 2.f); } m_relation_history_destroy(&rh); } TEST_CASE("RelationHistory") { using xrt::auxiliary::math::RelationHistory; RelationHistory rh; SECTION("empty buffer") { xrt_space_relation out_relation = XRT_SPACE_RELATION_ZERO; CHECK(rh.get(0, &out_relation) == M_RELATION_HISTORY_RESULT_INVALID); CHECK(rh.get(1, &out_relation) == M_RELATION_HISTORY_RESULT_INVALID); } SECTION("populated buffer") { xrt_space_relation relation = XRT_SPACE_RELATION_ZERO; relation.relation_flags = (xrt_space_relation_flags)( // XRT_SPACE_RELATION_POSITION_TRACKED_BIT | // XRT_SPACE_RELATION_POSITION_VALID_BIT | // XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT | // XRT_SPACE_RELATION_ORIENTATION_VALID_BIT | // XRT_SPACE_RELATION_LINEAR_VELOCITY_VALID_BIT); // relation.linear_velocity.x = 1.f; // arbitrary value constexpr auto T0 = 20 * (uint64_t)U_TIME_1S_IN_NS; // one second after T0 constexpr auto T1 = T0 + (uint64_t)U_TIME_1S_IN_NS; // two seconds after T0 constexpr auto T2 = T1 + (uint64_t)U_TIME_1S_IN_NS; xrt_space_relation out_relation = XRT_SPACE_RELATION_ZERO; uint64_t out_time = 0; CHECK(rh.size() == 0); CHECK_FALSE(rh.get_latest(&out_time, &out_relation)); CHECK(rh.push(relation, T0)); CHECK(rh.size() == 1); CHECK(rh.get_latest(&out_time, &out_relation)); CHECK(out_time == T0); relation.pose.position.x = 1.f; CHECK(rh.push(relation, T1)); CHECK(rh.size() == 2); CHECK(rh.get_latest(&out_time, &out_relation)); CHECK(out_time == T1); relation.pose.position.x = 2.f; CHECK(rh.push(relation, T2)); CHECK(rh.size() == 3); CHECK(rh.get_latest(&out_time, &out_relation)); CHECK(out_time == T2); // Try going back in time: should fail to push, leave state the same CHECK_FALSE(rh.push(relation, T1)); CHECK(rh.size() == 3); CHECK(rh.get_latest(&out_time, &out_relation)); CHECK(out_time == T2); CHECK(rh.get(0, &out_relation) == M_RELATION_HISTORY_RESULT_INVALID); CHECK(rh.get(T0, &out_relation) == M_RELATION_HISTORY_RESULT_EXACT); CHECK(out_relation.pose.position.x == 0.f); CHECK(rh.get(T1, &out_relation) == M_RELATION_HISTORY_RESULT_EXACT); CHECK(out_relation.pose.position.x == 1.f); CHECK(rh.get(T2, &out_relation) == M_RELATION_HISTORY_RESULT_EXACT); CHECK(out_relation.pose.position.x == 2.f); CHECK(rh.get(T0 - (uint64_t)U_TIME_1S_IN_NS, &out_relation) == M_RELATION_HISTORY_RESULT_REVERSE_PREDICTED); CHECK(out_relation.pose.position.x < 0.f); CHECK(rh.get((T0 + T1) / 2, &out_relation) == M_RELATION_HISTORY_RESULT_INTERPOLATED); CHECK(out_relation.pose.position.x > 0.f); CHECK(out_relation.pose.position.x < 1.f); CHECK(rh.get((T1 + T2) / 2, &out_relation) == M_RELATION_HISTORY_RESULT_INTERPOLATED); CHECK(out_relation.pose.position.x > 1.f); CHECK(out_relation.pose.position.x < 2.f); CHECK(rh.get(T2 + (uint64_t)U_TIME_1S_IN_NS, &out_relation) == M_RELATION_HISTORY_RESULT_PREDICTED); CHECK(out_relation.pose.position.x > 2.f); } } TEST_CASE("u_template_historybuf") { HistoryBuffer buffer; SECTION("behavior when empty") { CHECK(buffer.empty()); CHECK(0 == buffer.size()); // NOLINT CHECK_FALSE(buffer.begin().valid()); CHECK_FALSE(buffer.end().valid()); CHECK(buffer.begin() == buffer.end()); { INFO("Check after pop_back"); REQUIRE_FALSE(buffer.pop_back()); CHECK(buffer.empty()); } } SECTION("behavior with one") { buffer.push_back(0); CHECK_FALSE(buffer.empty()); CHECK(buffer.size() == 1); // check iterators CHECK(buffer.begin().valid()); CHECK_FALSE(buffer.end().valid()); CHECK_FALSE(buffer.begin() == buffer.end()); { auto it = buffer.end(); // should be permanently cleared ++it; CHECK_FALSE(it.valid()); --it; CHECK_FALSE(it.valid()); } CHECK(buffer.begin() == buffer.cbegin()); CHECK(buffer.end() == buffer.cend()); // can we decrement our past-the-end iterator to get the begin iterator? CHECK(buffer.begin() == --(buffer.end())); // make sure post-decrement works right CHECK_FALSE(buffer.begin() == (buffer.end())--); // make sure post-increment works right CHECK(buffer.begin() == buffer.begin()++); // make sure pre-increment works right CHECK_FALSE(buffer.begin() == ++(buffer.begin())); // check contents CHECK_NOTHROW(buffer.get_at_index(0)); CHECK_FALSE(buffer.get_at_index(0) == nullptr); CHECK(*buffer.get_at_index(0) == 0); CHECK_FALSE(buffer.get_at_age(0) == nullptr); CHECK(*buffer.get_at_age(0) == 0); CHECK_FALSE(buffer.get_at_clamped_age(0) == nullptr); CHECK(*buffer.get_at_clamped_age(0) == 0); CHECK(buffer.get_at_age(1) == nullptr); CHECK_FALSE(buffer.get_at_clamped_age(1) == nullptr); CHECK(*buffer.get_at_clamped_age(1) == 0); CHECK_FALSE(buffer.get_at_clamped_age(2) == nullptr); CHECK(*buffer.get_at_clamped_age(2) == 0); CHECK_NOTHROW(buffer.front()); CHECK(buffer.front() == 0); CHECK_NOTHROW(buffer.back()); CHECK(buffer.back() == 0); CHECK(*buffer.begin() == buffer.front()); { INFO("Check after pop_back"); REQUIRE(buffer.pop_back()); CHECK(buffer.size() == 0); REQUIRE_FALSE(buffer.pop_back()); } } SECTION("behavior with two") { buffer.push_back(0); buffer.push_back(1); CHECK_FALSE(buffer.empty()); CHECK(buffer.size() == 2); SECTION("check iterators") { // check iterators CHECK(buffer.begin().valid()); CHECK_FALSE(buffer.end().valid()); CHECK_FALSE(buffer.begin() == buffer.end()); { auto it = buffer.end(); // should be permanently cleared ++it; CHECK_FALSE(it.valid()); --it; CHECK_FALSE(it.valid()); } CHECK(buffer.begin() == buffer.cbegin()); CHECK(buffer.end() == buffer.cend()); // can we decrement our past-the-end iterator to get the begin iterator? CHECK(buffer.begin() == --(--(buffer.end()))); // make sure post-decrement works right CHECK_FALSE(buffer.begin() == (buffer.end())--); // make sure post-increment works right CHECK(buffer.begin() == buffer.begin()++); // make sure pre-increment works right CHECK_FALSE(buffer.begin() == ++(buffer.begin())); } SECTION("check contents") { // check contents CHECK_NOTHROW(buffer.get_at_index(0)); CHECK_FALSE(buffer.get_at_index(0) == nullptr); CHECK(*buffer.get_at_index(0) == 0); CHECK_FALSE(buffer.get_at_index(1) == nullptr); CHECK(*buffer.get_at_index(1) == 1); CHECK(buffer.get_at_index(2) == nullptr); CHECK_NOTHROW(buffer.get_at_age(0)); CHECK_FALSE(buffer.get_at_age(0) == nullptr); CHECK(*buffer.get_at_age(0) == 1); CHECK_FALSE(buffer.get_at_clamped_age(0) == nullptr); CHECK(*buffer.get_at_clamped_age(0) == 1); CHECK_FALSE(buffer.get_at_age(1) == nullptr); CHECK(*buffer.get_at_age(1) == 0); CHECK_FALSE(buffer.get_at_clamped_age(1) == nullptr); CHECK(*buffer.get_at_clamped_age(1) == 0); CHECK(buffer.get_at_age(2) == nullptr); CHECK_FALSE(buffer.get_at_clamped_age(2) == nullptr); CHECK(*buffer.get_at_clamped_age(2) == 0); CHECK_FALSE(buffer.get_at_clamped_age(3) == nullptr); CHECK(*buffer.get_at_clamped_age(3) == 0); CHECK_NOTHROW(buffer.front()); CHECK(buffer.front() == 0); CHECK_NOTHROW(buffer.back()); CHECK(buffer.back() == 1); CHECK(*buffer.begin() == buffer.front()); CHECK(buffer.back() == *(--buffer.end())); } SECTION("Check after pop_back") { REQUIRE(buffer.pop_back()); CHECK(buffer.size() == 1); CHECK(buffer.front() == 0); REQUIRE(buffer.pop_back()); CHECK(buffer.size() == 0); } } SECTION("algorithm behavior with 3") { buffer.push_back(0); buffer.push_back(2); buffer.push_back(4); CHECK_FALSE(buffer.empty()); CHECK(buffer.size() == 3); CHECK(buffer.begin() == std::find(buffer.begin(), buffer.end(), 0)); CHECK(++(buffer.begin()) == std::find(buffer.begin(), buffer.end(), 2)); CHECK(buffer.end() == std::find(buffer.begin(), buffer.end(), 5)); CHECK(++(buffer.begin()) == std::lower_bound(buffer.begin(), buffer.end(), 1)); } } TEST_CASE("IteratorBase") { HistoryBuffer buffer; buffer.push_back(0); buffer.push_back(2); buffer.push_back(4); using namespace xrt::auxiliary::util; using const_iterator = typename HistoryBuffer::const_iterator; const_iterator default_constructed{}; const_iterator begin_constructed = buffer.begin(); const_iterator end_constructed = buffer.end(); SECTION("Check default constructed") { CHECK_FALSE(default_constructed.valid()); CHECK(default_constructed.is_cleared()); } SECTION("Check begin constructed") { CHECK(begin_constructed.valid()); CHECK_FALSE(begin_constructed.is_cleared()); CHECK((--begin_constructed).is_cleared()); } SECTION("Check end constructed") { CHECK_FALSE(end_constructed.valid()); CHECK_FALSE(end_constructed.is_cleared()); // if we go past the end, we can go backwards into validity. CHECK_FALSE((++end_constructed).is_cleared()); } }