ifw-daq 3.1.0
IFW Data Acquisition modules
Loading...
Searching...
No Matches
testDpmDaqController.cpp
Go to the documentation of this file.
1/**
2 * @file
3 * @ingroup daq_ocm_libdaq_test
4 * @copyright 2022 ESO - European Southern Observatory
5 *
6 * @brief Unit test for `daq::DpmDaqController`
7 */
8#include <gtest/gtest.h>
9
10#include <daq/daqController.hpp>
11#include <mal/MalException.hpp>
12
14#include "statusObserver.hpp"
15#include "utils.hpp"
16
17using namespace ::testing;
18using namespace std::chrono;
19
20namespace daq {
21/**
22 * Fixture for daq::DaqController life cycle tests
23 *
24 * @ingroup daq_ocm_libdaq_test
25 */
26class TestDpmDaqController : public ::testing::Test {
27public:
29 : m_io_ctx()
30 , m_status(std::make_shared<ObservableStatus>("id", "fileid"))
31 , m_event_log(std::make_shared<ObservableEventLog>()) {
32 m_props.id = "id";
33 auto& s = m_props.meta_sources.emplace_back();
34 s.name = "meta";
35 s.rr_uri = "zpb.rr://127.0.0.1/daq";
36 m_props.results.emplace_back("location", "path");
37
38 // Default state
40 }
41
42 void SetUp() override {
44 m_dpm_client = std::make_shared<DpmClientMock>();
45 }
46
47 void PostSetUp() {
48 m_controller = std::make_shared<DpmDaqController>(
50 }
51
52 boost::asio::io_context m_io_ctx; // NOLINT
53 std::shared_ptr<ObservableStatus> m_status; // NOLINT
54 std::shared_ptr<ObservableEventLog> m_event_log; // NOLINT
56 std::shared_ptr<DpmClientMock> m_dpm_client; // NOLINT
57 /**
58 * Fake status update sample from DPM
59 */
60 Status m_dpm_status = Status("id", "fileid");
61
62 std::shared_ptr<DpmDaqController> m_controller;
63};
64
65TEST_F(TestDpmDaqController, StatusUpdateInNotScheduledSucceeds) {
66 // Setup
67 // New state will be Scheduled
68 m_dpm_status.state = State::Scheduled;
69 PostSetUp();
70
71 // Test
72 EXPECT_EQ(m_controller->GetState(), State::NotScheduled);
73 m_dpm_client->status_signal(m_dpm_status);
74 EXPECT_EQ(m_controller->GetState(), State::Scheduled)
75 << "Expected new state from status update signal";
76}
77
78/**
79 * Test that DPM can override status of OCM as it is the authoratitive source of DAQ status.
80 */
81TEST_F(TestDpmDaqController, StatusOverrideFromOcmAbortedToDpmMergingSucceeds) {
82 // Setup
83 // Put DAQ it in state Aborted with error from OCM point-of-view.
84 m_status->SetState(State::Aborted);
85 EXPECT_FALSE(m_status->HasError());
86 // The DPM state is Merging however.
87 m_dpm_status.state = State::Merging;
88 m_dpm_status.timestamp = Status::Clock::now();
89 PostSetUp();
90
91 // Test
92 // Update timestamp of status so it looks new
93 m_dpm_status.timestamp = Status::Clock::now();
94 m_dpm_status.alerts.push_back(MakeAlert("dpm", "oops", "Something went wrong"));
95 EXPECT_EQ(m_controller->GetState(), State::Aborted);
96 m_dpm_client->status_signal(m_dpm_status);
97 EXPECT_EQ(m_controller->GetState(), State::Merging)
98 << "Expected new state from status update signal";
99 EXPECT_TRUE(m_controller->GetErrorFlag());
100}
101
102TEST_F(TestDpmDaqController, StartAsyncThrows) {
103 PostSetUp();
104
105 auto fut = m_controller->StartAsync();
106 ASSERT_TRUE(fut.is_ready());
107 EXPECT_THROW(fut.get(), std::runtime_error);
108}
109
110TEST_F(TestDpmDaqController, StopAsyncThrows) {
111 PostSetUp();
112
113 auto fut = m_controller->StopAsync(ErrorPolicy::Tolerant);
114 ASSERT_TRUE(fut.is_ready());
115 EXPECT_THROW(fut.get(), std::runtime_error);
116}
117
118
119TEST_F(TestDpmDaqController, UpdateKeywordsThrows) {
120 PostSetUp();
121 EXPECT_THROW(m_controller->UpdateKeywords({}), std::runtime_error);
122}
123
124TEST_F(TestDpmDaqController, ScheduleMergeAsyncSucceedsIfDpmSucceeds) {
125 // Setup
126 PostSetUp();
127
128 using R = State;
129 boost::promise<R> reply;
130 EXPECT_CALL(*m_dpm_client, ScheduleAsync(_, _)).WillOnce(InvokeWithoutArgs([&] {
131 return reply.get_future();
132 }));
133
134 // Test
135 auto fut = m_controller->ScheduleMergeAsync();
136
137 // Fake reply
138 reply.set_value(State::Scheduled);
139 MakeTestProgress(m_io_ctx, &fut);
140
141 EXPECT_TRUE(fut.is_ready());
142 auto result = fut.get();
144 EXPECT_EQ(State::Scheduled, m_controller->GetState());
145}
146
148 ScheduleMergeAsyncSucceedsIfDpmSucceedsWithStatusSignalReceivedBeforeReply) {
149 // Setup
150 PostSetUp();
151
152 using R = State;
153 boost::promise<R> reply;
154 EXPECT_CALL(*m_dpm_client, ScheduleAsync(_, _)).WillOnce(InvokeWithoutArgs([&] {
155 return reply.get_future();
156 }));
157
158 // Test
159 auto fut = m_controller->ScheduleMergeAsync();
160
161 // Simulate status update from DPM before reply being received
162 m_dpm_status.state = State::Scheduled;
163 m_dpm_client->status_signal(m_dpm_status);
164 EXPECT_EQ(State::Scheduled, m_controller->GetState());
165
166 // Fake reply
167 reply.set_value(State::Scheduled);
168 MakeTestProgress(m_io_ctx, &fut);
169
170 EXPECT_TRUE(fut.is_ready());
171 auto result = fut.get();
173 EXPECT_EQ(State::Scheduled, m_controller->GetState());
174}
175
176TEST_F(TestDpmDaqController, ScheduleMergeAsyncFailsIfDpmFails) {
177 // Setup
178 PostSetUp();
179
180 using R = State;
181 boost::promise<R> reply;
182 EXPECT_CALL(*m_dpm_client, ScheduleAsync(_, _)).WillOnce(InvokeWithoutArgs([&] {
183 return reply.get_future();
184 }));
185
186 // Test
187 auto fut = m_controller->ScheduleMergeAsync();
188
189 // Fake reply
190 reply.set_exception(std::runtime_error("some_error"));
191 MakeTestProgress(m_io_ctx, &fut);
192
193 EXPECT_TRUE(fut.is_ready());
194 EXPECT_THROW(fut.get(), std::exception);
195 EXPECT_EQ(State::NotScheduled, m_controller->GetState());
196 EXPECT_TRUE(m_controller->GetStatus()->HasError());
197}
198
199TEST_F(TestDpmDaqController, ScheduleMergeAsyncFailsIfTimeout) {
200 // Setup
201 PostSetUp();
202
203 using R = State;
204 boost::promise<R> reply;
205 EXPECT_CALL(*m_dpm_client, ScheduleAsync(_, _)).WillOnce(InvokeWithoutArgs([&] {
206 return reply.get_future();
207 }));
208
209 // Test
210 auto fut = m_controller->ScheduleMergeAsync();
211 reply.set_exception(elt::mal::TimeoutException("TIMEOUT"));
212 MakeTestProgress(m_io_ctx, &fut);
213 EXPECT_TRUE(fut.is_ready());
214
215 // Fake reply
216 EXPECT_THROW(fut.get(), elt::mal::TimeoutException);
217 EXPECT_EQ(State::NotScheduled, m_controller->GetState()) << "State shoudln't have changed";
218 EXPECT_FALSE(m_controller->GetStatus()->HasError())
219 << "Error flag should not be set for timeouts";
220}
221
222TEST_F(TestDpmDaqController, ScheduleMergeAsyncFailsIfAlreadyScheduled) {
223 // Setup
224 m_status->SetState(State::Scheduled);
225 PostSetUp();
226
227 // Test
228 auto fut = m_controller->ScheduleMergeAsync();
229 EXPECT_TRUE(fut.is_ready());
230
231 // Fake reply
232 EXPECT_THROW(fut.get(), std::runtime_error);
233 EXPECT_EQ(State::Scheduled, m_controller->GetState());
234 EXPECT_FALSE(m_controller->GetStatus()->HasError());
235}
236
237TEST_F(TestDpmDaqController, AbortAsyncAbortImmediatelyIfNoPendingRequestsExist) {
238 // Setup
239 PostSetUp();
240
241 // Run
242 auto fut = m_controller->AbortAsync(ErrorPolicy::Strict);
243
244 EXPECT_TRUE(fut.is_ready());
245 auto result = fut.get();
246 EXPECT_EQ(State::Aborted, result.state);
247 EXPECT_FALSE(HasError(result));
248 EXPECT_EQ(State::Aborted, m_controller->GetState());
249}
250
251TEST_F(TestDpmDaqController, AbortAsyncWithStrictPolicyAbortsIfDpmAborts) {
252 // Setup
253 // Put it in state Scheduled or after
254 m_status->SetState(State::Scheduled);
255 PostSetUp();
256
257 using R = State;
258 boost::promise<R> reply;
259 EXPECT_CALL(*m_dpm_client, AbortAsync("id"))
260 .WillOnce(InvokeWithoutArgs([&] { return reply.get_future(); }));
261
262 // Run
263 auto fut = m_controller->AbortAsync(ErrorPolicy::Strict);
264
265 // Fake successful reply
266 reply.set_value(State::Aborted);
267
268 // Progress test
269 MakeTestProgress(m_io_ctx, &fut);
270
271 EXPECT_TRUE(fut.is_ready());
272 auto result = fut.get();
273 EXPECT_EQ(State::Aborted, result.state);
274 EXPECT_FALSE(HasError(result));
275 EXPECT_EQ(State::Aborted, m_controller->GetState());
276}
277
278TEST_F(TestDpmDaqController, AbortAsyncWithStrictPolicyDoesNothingIfDpmAbortFails) {
279 // Setup
280 m_status->SetState(State::Scheduled);
281 PostSetUp();
282
283 using R = State;
284 boost::promise<R> reply;
285 EXPECT_CALL(*m_dpm_client, AbortAsync("id"))
286 .WillOnce(InvokeWithoutArgs([&] { return reply.get_future(); }));
287
288 // Run
289 auto fut = m_controller->AbortAsync(ErrorPolicy::Strict);
290
291 // Fake reply
292 reply.set_exception(std::runtime_error("ERROR"));
293
294 // Progress test
295 MakeTestProgress(m_io_ctx, &fut);
296
297 EXPECT_TRUE(fut.is_ready());
298 EXPECT_THROW(fut.get(), std::runtime_error);
299 EXPECT_EQ(State::Scheduled, m_controller->GetState());
300 EXPECT_FALSE(m_controller->GetStatus()->HasError())
301 << "Failed to abort is not a condition for marking DAQ as error";
302}
303
304} // namespace daq
Stores data acquisition status and allows subscription to status changes.
Definition: eventLog.hpp:107
Stores data acquisition status and allows subscription to status changes.
Definition: status.hpp:224
daq::DpmClient
std::shared_ptr< ObservableStatus > m_status
boost::asio::io_context m_io_ctx
Status m_dpm_status
Fake status update sample from DPM.
std::shared_ptr< DpmClientMock > m_dpm_client
std::shared_ptr< DpmDaqController > m_controller
std::shared_ptr< ObservableEventLog > m_event_log
Fixture for daq::DaqController life cycle tests.
void MakeTestProgress(boost::asio::io_context &io_ctx, Future *fut=nullptr)
Test helper that progress the test by executing pending jobs and optionally wait for a future to be r...
Definition: utils.hpp:44
TEST_F(TestDpmClient, StartMonitoringSendsRequestAndReceivesReply)
bool HasError(Status const &status) noexcept
Definition: status.cpp:179
@ Strict
Any error is considered fatal and may lead to the operation being aborted.
@ Tolerant
Errors that can be ignored with partial completion of a command will be tolerated and is reported as ...
State
Observable states of the data acquisition process.
Definition: state.hpp:41
@ NotScheduled
Before daq is acknowledged by dpm it remains in NotScheduled.
@ Scheduled
daq is acknowledged by dpm and is scheduled for merging (i.e.
@ Aborted
Data acquisition has been aborted by user.
@ Merging
DAQ is being merged.
Alert MakeAlert(std::string_view category, std::string key, std::string description)
Construct alert.
Definition: status.cpp:45
Contains declaration for for DaqController.
Structure carrying context needed to start a Data Acquisition and construct a Data Product Specificat...
Definition: daqContext.hpp:42
std::vector< Source > meta_sources
Definition: daqContext.hpp:75
DpParts results
Results from Data Acquisition (FITS files and keywords).
Definition: daqContext.hpp:100
std::string id
DAQ identfier, possibly provided by user.
Definition: daqContext.hpp:58
Non observable status object that keeps stores status of data acquisition.
Definition: status.hpp:164
EXPECT_EQ(meta.rr_uri, "zpb.rr://meta")
ASSERT_TRUE(std::holds_alternative< OlasReceiver >(spec.receivers[0]))
Defines shared test utilities.