The open source OpenXR runtime
1// Copyright 2021, Collabora, Ltd.
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief HistoryBuffer collection tests.
6 * @author Rylie Pavlik <rylie.pavlik@collabora.com>
7 */
8
9#include <math/m_relation_history.h>
10#include <util/u_time.h>
11#include <util/u_template_historybuf.hpp>
12#include <iostream>
13
14
15using xrt::auxiliary::util::HistoryBuffer;
16
17template <typename Container>
18static inline std::ostream &
19operator<<(std::ostream &os, const xrt::auxiliary::util::RandomAccessIteratorBase<Container> &iter_base)
20{
21 os << "Iterator@[" << iter_base.index() << "]";
22 return os;
23}
24
25
26#include "catch_amalgamated.hpp"
27
28
29
30TEST_CASE("m_relation_history")
31{
32 m_relation_history *rh = nullptr;
33
34 m_relation_history_create(&rh);
35 SECTION("empty buffer")
36 {
37 xrt_space_relation out_relation = XRT_SPACE_RELATION_ZERO;
38 CHECK(m_relation_history_get(rh, 0, &out_relation) == M_RELATION_HISTORY_RESULT_INVALID);
39 CHECK(m_relation_history_get(rh, 1, &out_relation) == M_RELATION_HISTORY_RESULT_INVALID);
40 }
41 SECTION("populated buffer")
42 {
43 xrt_space_relation relation = XRT_SPACE_RELATION_ZERO;
44 relation.relation_flags = (xrt_space_relation_flags)( //
45 XRT_SPACE_RELATION_POSITION_TRACKED_BIT | //
46 XRT_SPACE_RELATION_POSITION_VALID_BIT | //
47 XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT | //
48 XRT_SPACE_RELATION_ORIENTATION_VALID_BIT | //
49 XRT_SPACE_RELATION_LINEAR_VELOCITY_VALID_BIT); //
50 relation.linear_velocity.x = 1.f;
51
52 // arbitrary value
53 constexpr auto T0 = 20 * (uint64_t)U_TIME_1S_IN_NS;
54 // one second after T0
55 constexpr auto T1 = T0 + (uint64_t)U_TIME_1S_IN_NS;
56 // two seconds after T0
57 constexpr auto T2 = T1 + (uint64_t)U_TIME_1S_IN_NS;
58
59 xrt_space_relation out_relation = XRT_SPACE_RELATION_ZERO;
60 int64_t out_time = 0;
61
62 CHECK(m_relation_history_get_size(rh) == 0);
63 CHECK_FALSE(m_relation_history_get_latest(rh, &out_time, &out_relation));
64
65
66 CHECK(m_relation_history_push(rh, &relation, T0));
67 CHECK(m_relation_history_get_size(rh) == 1);
68 CHECK(m_relation_history_get_latest(rh, &out_time, &out_relation));
69 CHECK(out_time == T0);
70
71 relation.pose.position.x = 1.f;
72 CHECK(m_relation_history_push(rh, &relation, T1));
73 CHECK(m_relation_history_get_size(rh) == 2);
74 CHECK(m_relation_history_get_latest(rh, &out_time, &out_relation));
75 CHECK(out_time == T1);
76
77 relation.pose.position.x = 2.f;
78 CHECK(m_relation_history_push(rh, &relation, T2));
79 CHECK(m_relation_history_get_size(rh) == 3);
80 CHECK(m_relation_history_get_latest(rh, &out_time, &out_relation));
81 CHECK(out_time == T2);
82
83 // Try going back in time: should fail to push, leave state the same
84 CHECK_FALSE(m_relation_history_push(rh, &relation, T1));
85 CHECK(m_relation_history_get_size(rh) == 3);
86 CHECK(m_relation_history_get_latest(rh, &out_time, &out_relation));
87 CHECK(out_time == T2);
88
89 CHECK(m_relation_history_get(rh, 0, &out_relation) == M_RELATION_HISTORY_RESULT_INVALID);
90
91 CHECK(m_relation_history_get(rh, T0, &out_relation) == M_RELATION_HISTORY_RESULT_EXACT);
92 CHECK(out_relation.pose.position.x == 0.f);
93
94 CHECK(m_relation_history_get(rh, T1, &out_relation) == M_RELATION_HISTORY_RESULT_EXACT);
95 CHECK(out_relation.pose.position.x == 1.f);
96
97 CHECK(m_relation_history_get(rh, T2, &out_relation) == M_RELATION_HISTORY_RESULT_EXACT);
98 CHECK(out_relation.pose.position.x == 2.f);
99
100
101 CHECK(m_relation_history_get(rh, T0 - (uint64_t)U_TIME_1S_IN_NS, &out_relation) ==
102 M_RELATION_HISTORY_RESULT_REVERSE_PREDICTED);
103 CHECK(out_relation.pose.position.x < 0.f);
104
105 CHECK(m_relation_history_get(rh, (T0 + T1) / 2, &out_relation) ==
106 M_RELATION_HISTORY_RESULT_INTERPOLATED);
107 CHECK(out_relation.pose.position.x > 0.f);
108 CHECK(out_relation.pose.position.x < 1.f);
109
110 CHECK(m_relation_history_get(rh, (T1 + T2) / 2, &out_relation) ==
111 M_RELATION_HISTORY_RESULT_INTERPOLATED);
112 CHECK(out_relation.pose.position.x > 1.f);
113 CHECK(out_relation.pose.position.x < 2.f);
114
115 CHECK(m_relation_history_get(rh, T2 + (uint64_t)U_TIME_1S_IN_NS, &out_relation) ==
116 M_RELATION_HISTORY_RESULT_PREDICTED);
117 CHECK(out_relation.pose.position.x > 2.f);
118 }
119
120
121 m_relation_history_destroy(&rh);
122}
123
124
125TEST_CASE("RelationHistory")
126{
127 using xrt::auxiliary::math::RelationHistory;
128 RelationHistory rh;
129
130 SECTION("empty buffer")
131 {
132 xrt_space_relation out_relation = XRT_SPACE_RELATION_ZERO;
133 CHECK(rh.get(0, &out_relation) == M_RELATION_HISTORY_RESULT_INVALID);
134 CHECK(rh.get(1, &out_relation) == M_RELATION_HISTORY_RESULT_INVALID);
135 }
136 SECTION("populated buffer")
137 {
138 xrt_space_relation relation = XRT_SPACE_RELATION_ZERO;
139 relation.relation_flags = (xrt_space_relation_flags)( //
140 XRT_SPACE_RELATION_POSITION_TRACKED_BIT | //
141 XRT_SPACE_RELATION_POSITION_VALID_BIT | //
142 XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT | //
143 XRT_SPACE_RELATION_ORIENTATION_VALID_BIT | //
144 XRT_SPACE_RELATION_LINEAR_VELOCITY_VALID_BIT); //
145 relation.linear_velocity.x = 1.f;
146
147 // arbitrary value
148 constexpr auto T0 = 20 * (uint64_t)U_TIME_1S_IN_NS;
149 // one second after T0
150 constexpr auto T1 = T0 + (uint64_t)U_TIME_1S_IN_NS;
151 // two seconds after T0
152 constexpr auto T2 = T1 + (uint64_t)U_TIME_1S_IN_NS;
153
154 xrt_space_relation out_relation = XRT_SPACE_RELATION_ZERO;
155 int64_t out_time = 0;
156
157 CHECK(rh.size() == 0);
158 CHECK_FALSE(rh.get_latest(&out_time, &out_relation));
159
160
161 CHECK(rh.push(relation, T0));
162 CHECK(rh.size() == 1);
163 CHECK(rh.get_latest(&out_time, &out_relation));
164 CHECK(out_time == T0);
165
166 relation.pose.position.x = 1.f;
167 CHECK(rh.push(relation, T1));
168 CHECK(rh.size() == 2);
169 CHECK(rh.get_latest(&out_time, &out_relation));
170 CHECK(out_time == T1);
171
172 relation.pose.position.x = 2.f;
173 CHECK(rh.push(relation, T2));
174 CHECK(rh.size() == 3);
175 CHECK(rh.get_latest(&out_time, &out_relation));
176 CHECK(out_time == T2);
177
178 // Try going back in time: should fail to push, leave state the same
179 CHECK_FALSE(rh.push(relation, T1));
180 CHECK(rh.size() == 3);
181 CHECK(rh.get_latest(&out_time, &out_relation));
182 CHECK(out_time == T2);
183
184 CHECK(rh.get(0, &out_relation) == M_RELATION_HISTORY_RESULT_INVALID);
185
186 CHECK(rh.get(T0, &out_relation) == M_RELATION_HISTORY_RESULT_EXACT);
187 CHECK(out_relation.pose.position.x == 0.f);
188
189 CHECK(rh.get(T1, &out_relation) == M_RELATION_HISTORY_RESULT_EXACT);
190 CHECK(out_relation.pose.position.x == 1.f);
191
192 CHECK(rh.get(T2, &out_relation) == M_RELATION_HISTORY_RESULT_EXACT);
193 CHECK(out_relation.pose.position.x == 2.f);
194
195
196 CHECK(rh.get(T0 - (uint64_t)U_TIME_1S_IN_NS, &out_relation) ==
197 M_RELATION_HISTORY_RESULT_REVERSE_PREDICTED);
198 CHECK(out_relation.pose.position.x < 0.f);
199
200 CHECK(rh.get((T0 + T1) / 2, &out_relation) == M_RELATION_HISTORY_RESULT_INTERPOLATED);
201 CHECK(out_relation.pose.position.x > 0.f);
202 CHECK(out_relation.pose.position.x < 1.f);
203
204 CHECK(rh.get((T1 + T2) / 2, &out_relation) == M_RELATION_HISTORY_RESULT_INTERPOLATED);
205 CHECK(out_relation.pose.position.x > 1.f);
206 CHECK(out_relation.pose.position.x < 2.f);
207
208 CHECK(rh.get(T2 + (uint64_t)U_TIME_1S_IN_NS, &out_relation) == M_RELATION_HISTORY_RESULT_PREDICTED);
209 CHECK(out_relation.pose.position.x > 2.f);
210 }
211}
212
213TEST_CASE("u_template_historybuf")
214{
215 HistoryBuffer<int, 4> buffer;
216 SECTION("behavior when empty")
217 {
218 CHECK(buffer.empty());
219 CHECK(0 == buffer.size()); // NOLINT
220 CHECK_FALSE(buffer.begin().valid());
221 CHECK_FALSE(buffer.end().valid());
222 CHECK(buffer.begin() == buffer.end());
223 {
224 INFO("Check after pop_back");
225 REQUIRE_FALSE(buffer.pop_back());
226 CHECK(buffer.empty());
227 }
228 }
229 SECTION("behavior with one")
230 {
231 buffer.push_back(0);
232 CHECK_FALSE(buffer.empty());
233 CHECK(buffer.size() == 1);
234
235 // check iterators
236 CHECK(buffer.begin().valid());
237 CHECK_FALSE(buffer.end().valid());
238 CHECK_FALSE(buffer.begin() == buffer.end());
239 {
240 auto it = buffer.end();
241 // should be permanently cleared
242 ++it;
243 CHECK_FALSE(it.valid());
244 --it;
245 CHECK_FALSE(it.valid());
246 }
247 CHECK(buffer.begin() == buffer.cbegin());
248 CHECK(buffer.end() == buffer.cend());
249
250 // can we decrement our past-the-end iterator to get the begin iterator?
251 CHECK(buffer.begin() == --(buffer.end()));
252
253 // make sure post-decrement works right
254 CHECK_FALSE(buffer.begin() == (buffer.end())--);
255
256 // make sure post-increment works right
257 CHECK(buffer.begin() == buffer.begin()++);
258
259 // make sure pre-increment works right
260 CHECK_FALSE(buffer.begin() == ++(buffer.begin()));
261
262
263 // check contents
264 CHECK_NOTHROW(buffer.get_at_index(0));
265 CHECK_FALSE(buffer.get_at_index(0) == nullptr);
266 CHECK(*buffer.get_at_index(0) == 0);
267
268 CHECK_FALSE(buffer.get_at_age(0) == nullptr);
269 CHECK(*buffer.get_at_age(0) == 0);
270 CHECK_FALSE(buffer.get_at_clamped_age(0) == nullptr);
271 CHECK(*buffer.get_at_clamped_age(0) == 0);
272
273 CHECK(buffer.get_at_age(1) == nullptr);
274
275 CHECK_FALSE(buffer.get_at_clamped_age(1) == nullptr);
276 CHECK(*buffer.get_at_clamped_age(1) == 0);
277
278 CHECK_FALSE(buffer.get_at_clamped_age(2) == nullptr);
279 CHECK(*buffer.get_at_clamped_age(2) == 0);
280
281 CHECK_NOTHROW(buffer.front());
282 CHECK(buffer.front() == 0);
283
284 CHECK_NOTHROW(buffer.back());
285 CHECK(buffer.back() == 0);
286
287 CHECK(*buffer.begin() == buffer.front());
288
289 {
290 INFO("Check after pop_back");
291 REQUIRE(buffer.pop_back());
292 CHECK(buffer.size() == 0);
293
294 REQUIRE_FALSE(buffer.pop_back());
295 }
296 }
297
298 SECTION("behavior with two")
299 {
300 buffer.push_back(0);
301 buffer.push_back(1);
302 CHECK_FALSE(buffer.empty());
303 CHECK(buffer.size() == 2);
304 SECTION("check iterators")
305 {
306 // check iterators
307 CHECK(buffer.begin().valid());
308 CHECK_FALSE(buffer.end().valid());
309 CHECK_FALSE(buffer.begin() == buffer.end());
310 {
311 auto it = buffer.end();
312 // should be permanently cleared
313 ++it;
314 CHECK_FALSE(it.valid());
315 --it;
316 CHECK_FALSE(it.valid());
317 }
318 CHECK(buffer.begin() == buffer.cbegin());
319 CHECK(buffer.end() == buffer.cend());
320
321 // can we decrement our past-the-end iterator to get the begin iterator?
322 CHECK(buffer.begin() == --(--(buffer.end())));
323
324 // make sure post-decrement works right
325 CHECK_FALSE(buffer.begin() == (buffer.end())--);
326
327 // make sure post-increment works right
328 CHECK(buffer.begin() == buffer.begin()++);
329
330 // make sure pre-increment works right
331 CHECK_FALSE(buffer.begin() == ++(buffer.begin()));
332 }
333 SECTION("check contents")
334 {
335 // check contents
336 CHECK_NOTHROW(buffer.get_at_index(0));
337 CHECK_FALSE(buffer.get_at_index(0) == nullptr);
338 CHECK(*buffer.get_at_index(0) == 0);
339 CHECK_FALSE(buffer.get_at_index(1) == nullptr);
340 CHECK(*buffer.get_at_index(1) == 1);
341 CHECK(buffer.get_at_index(2) == nullptr);
342
343 CHECK_NOTHROW(buffer.get_at_age(0));
344 CHECK_FALSE(buffer.get_at_age(0) == nullptr);
345 CHECK(*buffer.get_at_age(0) == 1);
346 CHECK_FALSE(buffer.get_at_clamped_age(0) == nullptr);
347 CHECK(*buffer.get_at_clamped_age(0) == 1);
348
349 CHECK_FALSE(buffer.get_at_age(1) == nullptr);
350 CHECK(*buffer.get_at_age(1) == 0);
351 CHECK_FALSE(buffer.get_at_clamped_age(1) == nullptr);
352 CHECK(*buffer.get_at_clamped_age(1) == 0);
353
354 CHECK(buffer.get_at_age(2) == nullptr);
355
356 CHECK_FALSE(buffer.get_at_clamped_age(2) == nullptr);
357 CHECK(*buffer.get_at_clamped_age(2) == 0);
358
359 CHECK_FALSE(buffer.get_at_clamped_age(3) == nullptr);
360 CHECK(*buffer.get_at_clamped_age(3) == 0);
361
362 CHECK_NOTHROW(buffer.front());
363 CHECK(buffer.front() == 0);
364
365 CHECK_NOTHROW(buffer.back());
366 CHECK(buffer.back() == 1);
367
368 CHECK(*buffer.begin() == buffer.front());
369 CHECK(buffer.back() == *(--buffer.end()));
370 }
371 SECTION("Check after pop_back")
372 {
373 REQUIRE(buffer.pop_back());
374 CHECK(buffer.size() == 1);
375 CHECK(buffer.front() == 0);
376
377 REQUIRE(buffer.pop_back());
378 CHECK(buffer.size() == 0);
379 }
380 }
381
382 SECTION("algorithm behavior with 3")
383 {
384 buffer.push_back(0);
385 buffer.push_back(2);
386 buffer.push_back(4);
387 CHECK_FALSE(buffer.empty());
388 CHECK(buffer.size() == 3);
389 CHECK(buffer.begin() == std::find(buffer.begin(), buffer.end(), 0));
390 CHECK(++(buffer.begin()) == std::find(buffer.begin(), buffer.end(), 2));
391 CHECK(buffer.end() == std::find(buffer.begin(), buffer.end(), 5));
392
393 CHECK(++(buffer.begin()) == std::lower_bound(buffer.begin(), buffer.end(), 1));
394 }
395}
396
397TEST_CASE("IteratorBase")
398{
399
400 HistoryBuffer<int, 4> buffer;
401 buffer.push_back(0);
402 buffer.push_back(2);
403 buffer.push_back(4);
404 using namespace xrt::auxiliary::util;
405 using const_iterator = typename HistoryBuffer<int, 4>::const_iterator;
406 const_iterator default_constructed{};
407 const_iterator begin_constructed = buffer.begin();
408 const_iterator end_constructed = buffer.end();
409
410 SECTION("Check default constructed")
411 {
412 CHECK_FALSE(default_constructed.valid());
413 CHECK(default_constructed.is_cleared());
414 }
415 SECTION("Check begin constructed")
416 {
417 CHECK(begin_constructed.valid());
418 CHECK_FALSE(begin_constructed.is_cleared());
419 CHECK((--begin_constructed).is_cleared());
420 }
421 SECTION("Check end constructed")
422 {
423 CHECK_FALSE(end_constructed.valid());
424 CHECK_FALSE(end_constructed.is_cleared());
425 // if we go past the end, we can go backwards into validity.
426 CHECK_FALSE((++end_constructed).is_cleared());
427 }
428}