ifw-daq  2.1.0-pre1
IFW Data Acquisition modules
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 
13 #include "mock/dpmClientMock.hpp"
14 #include "statusObserver.hpp"
15 #include "utils.hpp"
16 
17 using namespace ::testing;
18 using namespace std::chrono;
19 
20 namespace daq {
21 /**
22  * Fixture for daq::DaqController life cycle tests
23  *
24  * @ingroup daq_ocm_libdaq_test
25  */
26 class TestDpmDaqController : public ::testing::Test {
27 public:
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  // Default state
34  m_status->SetState(State::NotScheduled);
35  }
36 
37  void SetUp() override {
38  m_dpm_status = *m_status;
39  m_dpm_client = std::make_shared<DpmClientMock>();
40  }
41 
42  void PostSetUp() {
43  m_controller = std::make_shared<DpmDaqController>(
44  m_io_ctx, m_props, m_status, m_event_log, m_dpm_client);
45  }
46 
47  boost::asio::io_context m_io_ctx; // NOLINT
48  std::shared_ptr<ObservableStatus> m_status; // NOLINT
49  std::shared_ptr<ObservableEventLog> m_event_log; // NOLINT
50  DaqContext m_props; // NOLINT
51  std::shared_ptr<DpmClientMock> m_dpm_client; // NOLINT
52  /**
53  * Fake status update sample from DPM
54  */
55  Status m_dpm_status = Status("id", "fileid");
56 
57  std::shared_ptr<DpmDaqController> m_controller;
58 };
59 
60 TEST_F(TestDpmDaqController, StatusUpdateInNotScheduledSucceeds) {
61  // Setup
62  // New state will be Scheduled
63  m_dpm_status.state = State::Scheduled;
64  PostSetUp();
65 
66  // Test
67  EXPECT_EQ(m_controller->GetState(), State::NotScheduled);
68  m_dpm_client->status_signal(m_dpm_status);
69  EXPECT_EQ(m_controller->GetState(), State::Scheduled)
70  << "Expected new state from status update signal";
71 }
72 
73 /**
74  * Test that DPM can override status of OCM as it is the authoratitive source of DAQ status.
75  */
76 TEST_F(TestDpmDaqController, StatusOverrideFromOcmAbortedToDpmMergingSucceeds) {
77  // Setup
78  // Put DAQ it in state Aborted with error from OCM point-of-view.
79  m_status->SetState(State::Aborted, true);
80  // The DPM state is Merging however.
81  m_dpm_status.state = State::Merging;
82  PostSetUp();
83 
84  // Test
85  EXPECT_EQ(m_controller->GetState(), State::Aborted);
86  m_dpm_client->status_signal(m_dpm_status);
87  EXPECT_EQ(m_controller->GetState(), State::Merging)
88  << "Expected new state from status update signal";
89  EXPECT_FALSE(m_controller->GetErrorFlag());
90 }
91 
92 TEST_F(TestDpmDaqController, StartAsyncThrows) {
93  PostSetUp();
94 
95  auto fut = m_controller->StartAsync();
96  ASSERT_TRUE(fut.is_ready());
97  EXPECT_THROW(fut.get(), std::runtime_error);
98 }
99 
100 TEST_F(TestDpmDaqController, StopAsyncThrows) {
101  PostSetUp();
102 
103  auto fut = m_controller->StopAsync(ErrorPolicy::Tolerant);
104  ASSERT_TRUE(fut.is_ready());
105  EXPECT_THROW(fut.get(), std::runtime_error);
106 }
107 
108 
109 TEST_F(TestDpmDaqController, UpdateKeywordsThrows) {
110  PostSetUp();
111 
112  EXPECT_THROW(m_controller->UpdateKeywords({}), std::runtime_error);
113 }
114 
115 TEST_F(TestDpmDaqController, ScheduleMergeAsyncSucceedsIfDpmSucceeds) {
116  // Setup
117  PostSetUp();
118 
119  using R = State;
120  boost::promise<R> reply;
121  EXPECT_CALL(*m_dpm_client, ScheduleAsync(_)).WillOnce(InvokeWithoutArgs([&] {
122  return reply.get_future();
123  }));
124 
125  // Test
126  auto fut = m_controller->ScheduleMergeAsync();
127 
128  // Fake reply
129  reply.set_value(State::Scheduled);
130  MakeTestProgress(m_io_ctx, &fut);
131 
132  EXPECT_TRUE(fut.is_ready());
133  auto result = fut.get();
134  EXPECT_EQ(State::Scheduled, result);
135  EXPECT_EQ(State::Scheduled, m_controller->GetState());
136 }
137 
139  ScheduleMergeAsyncSucceedsIfDpmSucceedsWithStatusSignalReceivedBeforeReply) {
140  // Setup
141  PostSetUp();
142 
143  using R = State;
144  boost::promise<R> reply;
145  EXPECT_CALL(*m_dpm_client, ScheduleAsync(_)).WillOnce(InvokeWithoutArgs([&] {
146  return reply.get_future();
147  }));
148 
149  // Test
150  auto fut = m_controller->ScheduleMergeAsync();
151 
152  // Simulate status update from DPM before reply being received
153  m_dpm_status.state = State::Scheduled;
154  m_dpm_client->status_signal(m_dpm_status);
155  EXPECT_EQ(State::Scheduled, m_controller->GetState());
156 
157  // Fake reply
158  reply.set_value(State::Scheduled);
159  MakeTestProgress(m_io_ctx, &fut);
160 
161  EXPECT_TRUE(fut.is_ready());
162  auto result = fut.get();
163  EXPECT_EQ(State::Scheduled, result);
164  EXPECT_EQ(State::Scheduled, m_controller->GetState());
165 }
166 
167 TEST_F(TestDpmDaqController, ScheduleMergeAsyncFailsIfDpmFails) {
168  // Setup
169  PostSetUp();
170 
171  using R = State;
172  boost::promise<R> reply;
173  EXPECT_CALL(*m_dpm_client, ScheduleAsync(_)).WillOnce(InvokeWithoutArgs([&] {
174  return reply.get_future();
175  }));
176 
177  // Test
178  auto fut = m_controller->ScheduleMergeAsync();
179 
180  // Fake reply
181  reply.set_exception(std::runtime_error("some_error"));
182  MakeTestProgress(m_io_ctx, &fut);
183 
184  EXPECT_TRUE(fut.is_ready());
185  EXPECT_THROW(fut.get(), std::exception);
186  EXPECT_EQ(State::NotScheduled, m_controller->GetState());
187  EXPECT_TRUE(m_controller->GetStatus()->GetError());
188 }
189 
190 TEST_F(TestDpmDaqController, ScheduleMergeAsyncFailsIfTimeout) {
191  // Setup
192  PostSetUp();
193 
194  using R = State;
195  boost::promise<R> reply;
196  EXPECT_CALL(*m_dpm_client, ScheduleAsync(_)).WillOnce(InvokeWithoutArgs([&] {
197  return reply.get_future();
198  }));
199 
200  // Test
201  auto fut = m_controller->ScheduleMergeAsync();
202  reply.set_exception(elt::mal::TimeoutException("TIMEOUT"));
203  MakeTestProgress(m_io_ctx, &fut);
204  EXPECT_TRUE(fut.is_ready());
205 
206  // Fake reply
207  EXPECT_THROW(fut.get(), elt::mal::TimeoutException);
208  EXPECT_EQ(State::NotScheduled, m_controller->GetState()) << "State shoudln't have changed";
209  EXPECT_FALSE(m_controller->GetStatus()->GetError())
210  << "Error flag should not be set for timeouts";
211 }
212 
213 TEST_F(TestDpmDaqController, ScheduleMergeAsyncFailsIfAlreadyScheduled) {
214  // Setup
215  m_status->SetState(State::Scheduled);
216  PostSetUp();
217 
218  // Test
219  auto fut = m_controller->ScheduleMergeAsync();
220  EXPECT_TRUE(fut.is_ready());
221 
222  // Fake reply
223  EXPECT_THROW(fut.get(), std::runtime_error);
224  EXPECT_EQ(State::Scheduled, m_controller->GetState());
225  EXPECT_FALSE(m_controller->GetStatus()->GetError());
226 }
227 
228 TEST_F(TestDpmDaqController, AbortAsyncAbortImmediatelyIfNoPendingRequestsExist) {
229  // Setup
230  PostSetUp();
231 
232  // Run
233  auto fut = m_controller->AbortAsync(ErrorPolicy::Strict);
234 
235  EXPECT_TRUE(fut.is_ready());
236  auto result = fut.get();
237  EXPECT_EQ(State::Aborted, result.state);
238  EXPECT_FALSE(result.error);
239  EXPECT_EQ(State::Aborted, m_controller->GetState());
240 }
241 
242 TEST_F(TestDpmDaqController, AbortAsyncWithStrictPolicyAbortsIfDpmAborts) {
243  // Setup
244  // Put it in state Scheduled or after
245  m_status->SetState(State::Scheduled);
246  PostSetUp();
247 
248  using R = State;
249  boost::promise<R> reply;
250  EXPECT_CALL(*m_dpm_client, AbortAsync("id"))
251  .WillOnce(InvokeWithoutArgs([&] { return reply.get_future(); }));
252 
253  // Run
254  auto fut = m_controller->AbortAsync(ErrorPolicy::Strict);
255 
256  // Fake successful reply
257  reply.set_value(State::Aborted);
258 
259  // Progress test
260  MakeTestProgress(m_io_ctx, &fut);
261 
262  EXPECT_TRUE(fut.is_ready());
263  auto result = fut.get();
264  EXPECT_EQ(State::Aborted, result.state);
265  EXPECT_FALSE(result.error);
266  EXPECT_EQ(State::Aborted, m_controller->GetState());
267 }
268 
269 TEST_F(TestDpmDaqController, AbortAsyncWithStrictPolicyDoesNothingIfDpmAbortFails) {
270  // Setup
271  m_status->SetState(State::Scheduled);
272  PostSetUp();
273 
274  using R = State;
275  boost::promise<R> reply;
276  EXPECT_CALL(*m_dpm_client, AbortAsync("id"))
277  .WillOnce(InvokeWithoutArgs([&] { return reply.get_future(); }));
278 
279  // Run
280  auto fut = m_controller->AbortAsync(ErrorPolicy::Strict);
281 
282  // Fake reply
283  reply.set_exception(std::runtime_error("ERROR"));
284 
285  // Progress test
286  MakeTestProgress(m_io_ctx, &fut);
287 
288  EXPECT_TRUE(fut.is_ready());
289  EXPECT_THROW(fut.get(), std::runtime_error);
290  EXPECT_EQ(State::Scheduled, m_controller->GetState());
291  EXPECT_FALSE(m_controller->GetStatus()->GetError())
292  << "Failed to abort is not a condition for marking DAQ as error";
293 }
294 
295 } // namespace daq
daq::TestDpmDaqController::m_io_ctx
boost::asio::io_context m_io_ctx
Definition: testDpmDaqController.cpp:47
daq::State
State
Observable states of the data acquisition process.
Definition: state.hpp:39
utils.hpp
Defines shared test utilities.
daq::TestDpmDaqController::TestDpmDaqController
TestDpmDaqController()
Definition: testDpmDaqController.cpp:28
daq::TestDpmDaqController::m_props
DaqContext m_props
Definition: testDpmDaqController.cpp:50
daq::TestDpmDaqController::m_controller
std::shared_ptr< DpmDaqController > m_controller
Definition: testDpmDaqController.cpp:57
daq
Definition: asyncProcess.cpp:15
daq::ObservableStatus
Stores data acquisition status and allows subscription to status changes.
Definition: status.hpp:161
daq::DaqContext
Structure carrying context needed to start a Data Acquisition and construct a Data Product Specificat...
Definition: daqContext.hpp:48
daq::TEST_F
TEST_F(TestDpmDaqController, AbortAsyncWithStrictPolicyDoesNothingIfDpmAbortFails)
Definition: testDpmDaqController.cpp:269
daq::TestDpmDaqController::PostSetUp
void PostSetUp()
Definition: testDpmDaqController.cpp:42
daqController.hpp
Contains declaration for for DaqController.
statusObserver.hpp
daq::TestDpmDaqController::m_status
std::shared_ptr< ObservableStatus > m_status
Definition: testDpmDaqController.cpp:48
daq::Status
Non observable status object that keeps stores status of data acquisition.
Definition: status.hpp:120
daq::TestDpmDaqController::m_event_log
std::shared_ptr< ObservableEventLog > m_event_log
Definition: testDpmDaqController.cpp:49
daq::TestDpmDaqController::SetUp
void SetUp() override
Definition: testDpmDaqController.cpp:37
dpmClientMock.hpp
daq::DpmClient
daq::TestDpmDaqController::m_dpm_client
std::shared_ptr< DpmClientMock > m_dpm_client
Definition: testDpmDaqController.cpp:51
daq::ObservableEventLog
Stores data acquisition status and allows subscription to status changes.
Definition: eventLog.hpp:107
daq::TestDpmDaqController
Fixture for daq::DaqController life cycle tests.
Definition: testDpmDaqController.cpp:26
MakeTestProgress
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:42