Line data Source code
1 : /**
2 : * @file TimestampEstimatorSystem_test.cxx TimestampEstimatorSystem class Unit Tests
3 : *
4 : * This is part of the DUNE DAQ Application Framework, copyright 2020.
5 : * Licensing/copyright details are in the COPYING file that you should have
6 : * received with this code.
7 : */
8 :
9 : #include "utilities/TimestampEstimatorSystem.hpp"
10 :
11 : /**
12 : * @brief Name of this test module
13 : */
14 : #define BOOST_TEST_MODULE TimestampEstimatorSystem_test // NOLINT
15 :
16 : #include "boost/test/unit_test.hpp"
17 : #include <boost/test/tools/old/interface.hpp>
18 : #include <chrono>
19 : #include <future>
20 :
21 : using namespace dunedaq;
22 :
23 : BOOST_AUTO_TEST_SUITE(BOOST_TEST_MODULE)
24 :
25 2 : BOOST_AUTO_TEST_CASE(Basics)
26 : {
27 1 : const uint64_t clock_frequency_hz = 62'500'000; // NOLINT(build/unsigned)
28 1 : std::atomic<bool> continue_flag{ true };
29 1 : dunedaq::utilities::TimestampEstimatorSystem tes(clock_frequency_hz);
30 :
31 1 : BOOST_CHECK_EQUAL(tes.wait_for_valid_timestamp(continue_flag), dunedaq::utilities::TimestampEstimatorBase::kFinished);
32 :
33 1 : std::atomic<bool> do_not_continue_flag{ false };
34 1 : BOOST_CHECK_EQUAL(tes.wait_for_valid_timestamp(do_not_continue_flag),
35 : dunedaq::utilities::TimestampEstimatorBase::kFinished);
36 :
37 1 : auto ts_now = tes.get_timestamp_estimate();
38 1 : BOOST_CHECK_EQUAL(tes.wait_for_requested_timestamp(ts_now + clock_frequency_hz, continue_flag),
39 : dunedaq::utilities::TimestampEstimatorBase::kFinished);
40 :
41 1 : ts_now = tes.get_timestamp_estimate();
42 1 : BOOST_CHECK_EQUAL(tes.wait_for_requested_timestamp(ts_now + clock_frequency_hz, do_not_continue_flag),
43 : dunedaq::utilities::TimestampEstimatorBase::kInterrupted);
44 :
45 : // Check that the timestamp doesn't go backwards
46 1 : auto ts1 = tes.get_timestamp_estimate();
47 1 : auto ts2 = tes.get_timestamp_estimate();
48 1 : BOOST_CHECK_GE(ts2, ts1);
49 1 : }
50 :
51 : // 27-May-2025, KAB: this test case is intended to verify that an instance of
52 : // the TimestampEstimatorSystem behaves as expected when it first starts up.
53 2 : BOOST_AUTO_TEST_CASE(StartupBehavior)
54 : {
55 1 : using namespace std::chrono;
56 1 : using namespace std::chrono_literals;
57 :
58 1 : const uint64_t clock_frequency_hz = 62'500'000; // NOLINT(build/unsigned)
59 1 : const double clock_frequency_Mhz = 62.5;
60 :
61 1 : utilities::TimestampEstimatorSystem te(clock_frequency_hz);
62 :
63 1 : auto system_time_start =
64 1 : static_cast<uint64_t>(duration_cast<microseconds>(system_clock::now().time_since_epoch()).count()); // NOLINT
65 1 : auto steady_time_start =
66 1 : static_cast<uint64_t>(duration_cast<microseconds>(steady_clock::now().time_since_epoch()).count()); // NOLINT
67 1 : auto daq_time_start =
68 1 : static_cast<uint64_t>((clock_frequency_Mhz) * static_cast<double>(system_time_start)); // NOLINT(build/unsigned)
69 :
70 1 : std::atomic<bool> do_not_continue_flag{ false };
71 1 : std::atomic<bool> do_continue_flag{ true };
72 1 : std::atomic<bool> continue_flag_for_thread{ true };
73 1 : std::atomic<bool> thread_has_finished{ false };
74 1 : std::atomic<utilities::TimestampEstimatorBase::WaitStatus> return_code_from_thread_wait{
75 : dunedaq::utilities::TimestampEstimatorBase::kInterrupted
76 : };
77 :
78 : // spawn a thread that waits until the TSE can provide a valid timestamp
79 3 : std::function<void()> valid_timestamp_wait_func = [&]() {
80 1 : return_code_from_thread_wait = te.wait_for_valid_timestamp(continue_flag_for_thread);
81 1 : thread_has_finished = true;
82 2 : };
83 1 : auto wait_ftr = std::async(std::launch::async, valid_timestamp_wait_func);
84 :
85 : // TimestampEstimatorSystem always provides valid timestamps
86 1 : std::this_thread::sleep_for(100ms);
87 1 : BOOST_CHECK_EQUAL(thread_has_finished, true);
88 :
89 : // TimestampEstimatorSystem always should return kFinished
90 1 : BOOST_CHECK_EQUAL(te.wait_for_valid_timestamp(do_not_continue_flag),
91 : dunedaq::utilities::TimestampEstimatorBase::kFinished);
92 1 : BOOST_CHECK_EQUAL(thread_has_finished, true);
93 :
94 : // verify that the wait thread has finished and it received the expected return code
95 1 : BOOST_CHECK_EQUAL(thread_has_finished, true);
96 1 : BOOST_CHECK_EQUAL(return_code_from_thread_wait, dunedaq::utilities::TimestampEstimatorBase::kFinished);
97 : // if the thread has not finished, tell it to finish now
98 1 : if (!thread_has_finished) {
99 0 : continue_flag_for_thread.store(false);
100 : }
101 :
102 : // verify that the TSE instance provides valid timestamps and those
103 : // timestamp track closely to wallclock time (computer system time)
104 11 : for (size_t i = 0; i < 10; ++i) {
105 :
106 10 : std::this_thread::sleep_for(100ms);
107 10 : auto steady_now =
108 10 : static_cast<uint64_t>(duration_cast<microseconds>(steady_clock::now().time_since_epoch()).count()); // NOLINT
109 10 : uint64_t te_now = te.get_timestamp_estimate(); // NOLINT(build/unsigned)
110 10 : auto steady_diff = static_cast<int64_t>(steady_now - steady_time_start);
111 10 : auto te_diff = static_cast<int64_t>(te_now - daq_time_start);
112 10 : auto dd = static_cast<int64_t>(te_diff - (steady_diff * clock_frequency_hz / 1'000'000));
113 :
114 10 : BOOST_CHECK_LT(abs(dd), 1'000);
115 : }
116 :
117 : // now the TSE instance "wait" method should return immediately with a status
118 : // that indicates that it *does* have a valid timestamp, independent of whether
119 : // we tell it to wait for a valid timestamp or not
120 1 : BOOST_CHECK_EQUAL(te.wait_for_valid_timestamp(do_not_continue_flag),
121 : dunedaq::utilities::TimestampEstimatorBase::kFinished);
122 1 : BOOST_CHECK_EQUAL(te.wait_for_valid_timestamp(do_continue_flag),
123 : dunedaq::utilities::TimestampEstimatorBase::kFinished);
124 1 : }
125 :
126 1 : BOOST_AUTO_TEST_CASE(AdditionalTestIdeas)
127 : {
128 : // slow clock
129 : // fast clock
130 : // non-standard clock frequency
131 : // bursts and delays
132 0 : }
133 :
134 : BOOST_AUTO_TEST_SUITE_END()
|