ifw-daq  2.1.0-pre1
IFW Data Acquisition modules
testManager.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 daq::ManagerImpl tests
7  */
8 #include <daq/manager.hpp>
9 #include <fmt/ostream.h>
10 #include <regex>
11 
12 #include "mock/daqController.hpp"
13 #include "mock/mockWorkspace.hpp"
14 #include "statusObserver.hpp"
15 #include "utils.hpp"
16 #include <gtest/gtest.h>
17 #include <log4cplus/loggingmacros.h>
18 
19 #include <daq/error/report.hpp>
20 
21 using namespace daq;
22 using namespace ::testing;
23 using namespace std::literals::string_view_literals;
24 using namespace std::chrono_literals;
25 
26 struct FixtureBase : ::testing::Test {
27  template <class T>
28  T ExpectNoException(boost::future<T>& f) {
29  if (!f.is_ready()) {
30  ADD_FAILURE() << "Future is not ready";
31  throw std::runtime_error("test failure");
32  }
33  try {
34  return f.get();
35  } catch (...) {
36  LOG4CPLUS_ERROR("test",
37  "Future contained exception\n"
38  << daq::error::NestedExceptionReporter(std::current_exception()));
39  throw;
40  }
41  }
42  ManagerParams m_params = {"INS"};
43 };
44 
45 /**
46  * @ingroup daq_ocm_libdaq_test
47  */
49 public:
50  void SetUp() override {
51  }
52  void TearDown() override {
53  }
54 };
55 
56 /**
57  * @ingroup daq_ocm_libdaq_test
58  */
59 class TestManagerImpl : public FixtureBase {
60 public:
62  : m_io_ctx()
63  , m_executor(m_io_ctx)
64  , m_event_log(std::make_shared<daq::ObservableEventLog>())
65  , m_manager(m_executor, m_params, m_workspace, m_event_log, m_daq_factory) {
66  }
67  /**
68  * Creates manager and adds two data acquisitions.
69  */
70  void SetUp() override {
71  m_daq_id_1 = "daq1";
72  m_daq_id_2 = "daq2";
73  m_daq_ctx_1.id = m_daq_id_1;
74  m_daq_ctx_1.file_id = "fileid1";
75  m_daq_ctx_2.id = m_daq_id_2;
76  m_daq_ctx_2.file_id = "fileid2";
77 
78  m_daq1_status = std::make_shared<ObservableStatus>(m_daq_id_1, "fileid1");
79  m_daq2_status = std::make_shared<ObservableStatus>(m_daq_id_2, "fileid2");
80  m_daq1 = std::make_shared<DaqControllerMock>();
81  m_daq2 = std::make_shared<DaqControllerMock>();
82  m_daq_factory.ocm_mocks["daq1"] = m_daq1;
83  m_daq_factory.ocm_mocks["daq2"] = m_daq2;
84 
85  m_dpm_daq1 = std::make_shared<DaqControllerMock>();
86  m_dpm_daq2 = std::make_shared<DaqControllerMock>();
87  m_daq_factory.dpm_mocks["daq1"] = m_dpm_daq1;
88  m_daq_factory.dpm_mocks["daq2"] = m_dpm_daq2;
89 
90  EXPECT_CALL(*m_daq1, GetId()).Times(AnyNumber()).WillRepeatedly(ReturnRef(m_daq_id_1));
91  EXPECT_CALL(*m_daq2, GetId()).Times(AnyNumber()).WillRepeatedly(ReturnRef(m_daq_id_2));
92  EXPECT_CALL(*m_daq1, GetStatus()).Times(AnyNumber()).WillRepeatedly(Return(m_daq1_status));
93  EXPECT_CALL(*m_daq2, GetStatus()).Times(AnyNumber()).WillRepeatedly(Return(m_daq2_status));
94  EXPECT_CALL(Const(*m_daq1), GetStatus())
95  .Times(AnyNumber())
96  .WillRepeatedly(Return(m_daq1_status));
97  EXPECT_CALL(Const(*m_daq2), GetStatus())
98  .Times(AnyNumber())
99  .WillRepeatedly(Return(m_daq2_status));
100  EXPECT_CALL(Const(*m_daq1), GetContext())
101  .Times(AnyNumber())
102  .WillRepeatedly(ReturnRef(m_daq_ctx_1));
103 
104  m_daq1_dpm_status = std::make_shared<ObservableStatus>(m_daq_id_1, "fileid1");
105  m_daq1_dpm_status->SetState(State::Transferring);
106  m_daq2_dpm_status = std::make_shared<ObservableStatus>(m_daq_id_2, "fileid2");
107  m_daq2_dpm_status->SetState(State::Merging);
108 
109  EXPECT_CALL(*m_dpm_daq1, GetId()).WillRepeatedly(ReturnRef(m_daq_id_1));
110  EXPECT_CALL(*m_dpm_daq1, GetStatus())
111  .Times(AnyNumber())
112  .WillRepeatedly(Return(m_daq1_status));
113  EXPECT_CALL(Const(*m_dpm_daq1), GetStatus())
114  .Times(AnyNumber())
115  .WillRepeatedly(Return(m_daq1_status));
116  EXPECT_CALL(Const(*m_dpm_daq1), GetContext())
117  .Times(AnyNumber())
118  .WillRepeatedly(ReturnRef(m_daq_ctx_1));
119 
120  EXPECT_CALL(*m_dpm_daq2, GetId()).WillRepeatedly(ReturnRef(m_daq_id_2));
121  EXPECT_CALL(*m_dpm_daq2, GetStatus())
122  .Times(AnyNumber())
123  .WillRepeatedly(Return(m_daq2_dpm_status));
124  EXPECT_CALL(Const(*m_dpm_daq2), GetStatus())
125  .Times(AnyNumber())
126  .WillRepeatedly(Return(m_daq2_dpm_status));
127  EXPECT_CALL(Const(*m_dpm_daq2), GetContext())
128  .Times(AnyNumber())
129  .WillRepeatedly(ReturnRef(m_daq_ctx_2));
130  }
131 
132  auto StartDaq1() -> boost::future<State> {
133  EXPECT_CALL(*m_daq1, StartAsync())
134  .WillOnce(Return(ByMove(boost::make_ready_future<State>(m_daq1_status->GetState()))));
135  EXPECT_CALL(m_workspace, StoreList(std::vector<std::string>({"daq1"})));
136  EXPECT_CALL(m_workspace, StoreStatus(Field(&Status::id, "daq1"))).Times(AtLeast(1));
137  EXPECT_CALL(m_workspace, StoreContext(_));
138  return m_manager.StartDaqAsync(m_daq_ctx_1);
139  }
140 
141  void TearDown() override {
142  // Run any pending handlers to allow mocks to expire without leaking
143  m_io_ctx.restart();
144  m_io_ctx.poll();
145 
146  m_daq_factory.ocm_mocks.clear();
147  m_daq_factory.dpm_mocks.clear();
148  }
149 
150  boost::asio::io_context m_io_ctx;
153  std::shared_ptr<daq::ObservableEventLog> m_event_log;
154  std::string m_daq_id_1;
155  std::string m_daq_id_2;
158  std::shared_ptr<DaqControllerMock> m_daq1;
159  std::shared_ptr<DaqControllerMock> m_daq2;
160  std::shared_ptr<DaqControllerMock> m_dpm_daq1;
161  std::shared_ptr<DaqControllerMock> m_dpm_daq2;
162  std::shared_ptr<ObservableStatus> m_daq1_status;
163  std::shared_ptr<ObservableStatus> m_daq2_status;
164  std::shared_ptr<ObservableStatus> m_daq1_dpm_status;
165  std::shared_ptr<ObservableStatus> m_daq2_dpm_status;
168 };
169 
170 TEST_F(TestManagerImplLifecycle, AddDaqNotifiesObserver) {
171  // Setup
172  boost::asio::io_context io_ctx;
173  rad::IoExecutor executor(io_ctx);
174  DaqControllerFactoryFake factory;
175  auto event_log = std::make_shared<ObservableEventLog>();
176  DaqContext daq_ctx;
177  MockWorkspace workspace;
178 
179  ManagerImpl mgr(executor, m_params, workspace, event_log, factory);
180  auto id = std::string("id");
181  auto status = std::make_shared<ObservableStatus>(id, "fileid");
182  daq_ctx.id = id;
183  daq_ctx.file_id = id;
184  auto daq = std::make_shared<DaqControllerMock>();
185  factory.ocm_mocks[id] = daq;
186 
187  EXPECT_CALL(*daq, StartAsync())
188  .WillOnce(Return(ByMove(boost::make_ready_future<State>(status->GetState()))));
189  EXPECT_CALL(*daq, GetId()).WillRepeatedly(ReturnRef(id));
190  EXPECT_CALL(*daq, GetStatus()).WillRepeatedly(Return(status));
191  EXPECT_CALL(Const(*daq), GetStatus()).WillRepeatedly(Return(status));
192  EXPECT_CALL(Const(*daq), GetContext()).Times(AnyNumber()).WillRepeatedly(ReturnRef(daq_ctx));
193 
194  EXPECT_CALL(workspace, StoreList(std::vector<std::string>({"id"})));
195  EXPECT_CALL(workspace, StoreStatus(Field(&Status::id, "id")));
196  EXPECT_CALL(workspace, StoreContext(daq_ctx));
197 
199  EXPECT_CALL(o, CallOperator(_));
200  mgr.GetStatusSignal().ConnectObserver(std::reference_wrapper(o));
201 
202  // Run
203  auto f = mgr.StartDaqAsync(daq_ctx);
204  io_ctx.poll();
205  ExpectNoException(f);
206 }
207 
208 TEST_F(TestManagerImplLifecycle, AwaitStateCompletesWithAbandonedManager) {
209  // Setup
210  boost::asio::io_context io_ctx;
211  rad::IoExecutor executor(io_ctx);
212  DaqControllerFactoryFake factory;
213  auto event_log = std::make_shared<ObservableEventLog>();
214  DaqContext daq_ctx;
215  MockWorkspace workspace;
216 
217  auto id = std::string("id");
218  daq_ctx.id = id;
219  daq_ctx.file_id = id;
220  auto status = std::make_shared<ObservableStatus>(id, "fileid");
221  auto daq = std::make_shared<DaqControllerMock>();
222  factory.ocm_mocks[id] = daq;
223  EXPECT_CALL(*daq, StartAsync())
224  .WillOnce(Return(ByMove(boost::make_ready_future<State>(status->GetState()))));
225  EXPECT_CALL(*daq, GetId()).Times(AnyNumber()).WillRepeatedly(ReturnRef(id));
226  EXPECT_CALL(*daq, GetStatus()).Times(AnyNumber()).WillRepeatedly(Return(status));
227  EXPECT_CALL(Const(*daq), GetStatus()).Times(AnyNumber()).WillRepeatedly(Return(status));
228  EXPECT_CALL(Const(*daq), GetContext()).Times(AnyNumber()).WillRepeatedly(ReturnRef(daq_ctx));
229 
230  EXPECT_CALL(workspace, StoreList(std::vector<std::string>({"id"})));
231  EXPECT_CALL(workspace, StoreStatus(Field(&Status::id, "id")));
232  EXPECT_CALL(workspace, StoreContext(daq_ctx));
233 
234  boost::future<Result<Status>> res;
235  {
236  ManagerImpl mgr(executor, m_params, workspace, event_log, factory);
237  mgr.StartDaqAsync(daq_ctx);
238 
239  // Initiate await that should be aborted when manager is destroyed
240  mgr.AwaitDaqStateAsync("id"sv, State::Acquiring, 5ms).swap(res);
241  ASSERT_FALSE(res.is_ready());
242  }
243 
244  // Run
245  MakeTestProgress(io_ctx, &res);
246  ASSERT_TRUE(res.is_ready());
247  EXPECT_THROW(res.get(), DaqOperationAborted);
248 }
249 
251  {
252  std::regex regex("INSTR\\.\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}");
253  auto id = MakeIdCandidate("INSTRUMENT", 0);
254  EXPECT_TRUE(std::regex_match(id, regex))
255  << "Instrument ID should be truncated to 5 characters if too long";
256  }
257  {
258  std::regex regex("INS\\.\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}");
259  auto id = m_manager.MakeDaqId();
260  EXPECT_TRUE(std::regex_match(id, regex));
261  }
262  {
263  auto id1 = MakeIdCandidate("INSTRUMENT", 0);
264  auto id2 = MakeIdCandidate("INSTRUMENT", 1);
265  EXPECT_NE(id1, id2) << "Adding jitter should have made the ID different";
266  }
267 }
268 
269 TEST_F(TestManagerImpl, StartDaqWithSameIdThrowsInvalidArgument) {
270  // Setup
271  EXPECT_CALL(*m_daq1, StartAsync())
272  .WillOnce(Return(ByMove(boost::make_ready_future<State>(State::Starting))));
273  EXPECT_CALL(m_workspace, StoreList(std::vector<std::string>({"daq1"})));
274  EXPECT_CALL(m_workspace, StoreStatus(Field(&Status::id, "daq1")));
275  EXPECT_CALL(m_workspace, StoreContext(_));
276 
277  m_manager.StartDaqAsync(m_daq_ctx_1);
278 
279  // Tes
280  auto f = m_manager.StartDaqAsync(m_daq_ctx_1);
281  m_io_ctx.poll();
282  ASSERT_TRUE(f.is_ready());
283  EXPECT_THROW(f.get(), std::invalid_argument);
284 }
285 
286 TEST_F(TestManagerImpl, StartDaqAsyncStartsDaq) {
287  // Setup
288  EXPECT_CALL(*m_daq1, StartAsync())
289  .WillOnce(Return(ByMove(boost::make_ready_future<State>(State::Starting))));
290  EXPECT_CALL(m_workspace, StoreList(std::vector<std::string>({"daq1"})));
291  EXPECT_CALL(m_workspace, StoreStatus(Field(&Status::id, "daq1")));
292  EXPECT_CALL(m_workspace, StoreContext(_));
293 
294  // Run
295  auto fut = m_manager.StartDaqAsync(m_daq_ctx_1);
296  m_io_ctx.poll();
297  ASSERT_TRUE(fut.is_ready());
298  EXPECT_EQ(fut.get(), State::Starting);
299 }
300 
301 /**
302  * Tests the behaviour that if DaqController::StartAsync() fails it should automatically
303  * force-abort the acquisition. This is mainly done because putting that responsibility on the user
304  * is not productive.
305  *
306  * note: This behaviour can be made configurable.
307  */
308 TEST_F(TestManagerImpl, StartDaqAbortsIfDaqControllerFails) {
309  // Setup
310  EXPECT_CALL(*m_daq1, StartAsync())
311  .WillOnce(Return(
312  ByMove(boost::make_exceptional_future<State>(std::runtime_error("START FAILED")))));
313  boost::promise<daq::Status> abort_reply;
314  EXPECT_CALL(*m_daq1, AbortAsync(ErrorPolicy::Tolerant))
315  .WillOnce(Return(ByMove(abort_reply.get_future())));
316  EXPECT_CALL(m_workspace, StoreList(std::vector<std::string>({"daq1"})));
317  EXPECT_CALL(m_workspace, StoreStatus(Field(&Status::id, "daq1")));
318  EXPECT_CALL(m_workspace, StoreContext(_));
319 
320  // Test
321  auto f = m_manager.StartDaqAsync(m_daq_ctx_1);
322  m_io_ctx.poll();
323  ASSERT_FALSE(f.is_ready());
324 
325  // Send reply
326  abort_reply.set_exception(std::logic_error("ABORT ALSO FAILED"));
327 
328  MakeTestProgress(m_io_ctx, &f);
329  ASSERT_TRUE(f.is_ready());
330  EXPECT_THROW(f.get(), std::runtime_error);
331 }
332 
333 TEST_F(TestManagerImpl, StopNonexistingDaqThrowsInvalidArgument) {
334  auto fut = m_manager.StopDaqAsync("nonexistant-id"sv, daq::ErrorPolicy::Strict);
335  ASSERT_TRUE(fut.is_ready());
336  EXPECT_THROW(fut.get(), std::invalid_argument);
337 }
338 
339 TEST_F(TestManagerImpl, StopDaqAsyncStopsDaq) {
340  // Setup
341  StartDaq1();
342 
344  EXPECT_CALL(*m_daq1, StopAsync(daq::ErrorPolicy::Strict))
345  .WillOnce(Return(ByMove(
346  boost::make_ready_future<Status>(Status("daq1", "fileid", State::Stopped, false, t)))));
347 
348  // Run
349  auto fut = m_manager.StopDaqAsync("daq1"sv, daq::ErrorPolicy::Strict);
350  auto status = ExpectNoException(fut);
351  EXPECT_EQ(status.state, State::Stopped);
352  EXPECT_EQ(status.error, false);
353 }
354 
355 TEST_F(TestManagerImpl, AbortNonexistingDaqThrowsInvalidArgumentEvenIfTolerant) {
356  auto fut = m_manager.AbortDaqAsync("nonexistant-id"sv, ErrorPolicy::Tolerant);
357  ASSERT_TRUE(fut.is_ready());
358  EXPECT_THROW(fut.get(), std::invalid_argument);
359 }
360 
361 TEST_F(TestManagerImpl, AbortDaqAsyncAbortsDaq) {
362  // Setup
363  StartDaq1();
364 
365  auto reply_status = daq::Status("daq1", "fileid");
366  reply_status.state = State::AbortingAcquiring;
367  EXPECT_CALL(*m_daq1, AbortAsync(ErrorPolicy::Strict))
368  .WillOnce(Return(ByMove(boost::make_ready_future<Status>(reply_status))));
369 
370  // Run
371  auto fut = m_manager.AbortDaqAsync("daq1"sv, ErrorPolicy::Strict);
372  auto result = ExpectNoException(fut);
373  EXPECT_EQ(result.state, State::AbortingAcquiring);
374  EXPECT_FALSE(result.error);
375 }
376 
377 TEST_F(TestManagerImpl, UpdateKeywordsUpdatesKeywords) {
378  // Setup
379  StartDaq1();
380 
381  daq::fits::KeywordVector keywords = {
382  daq::fits::ValueKeyword("OBJECT", "OBJECT,SKY"),
383  daq::fits::EsoKeyword("OBS TPLNO", static_cast<uint64_t>(2))};
384  EXPECT_CALL(*m_daq1, UpdateKeywords(keywords));
385 
386  // Run
387  m_manager.UpdateKeywords("daq1"sv, keywords);
388 }
389 
390 TEST_F(TestManagerImpl, UpdateKeywordsForNonexistingDaqThrowsInvalidArgument) {
391  daq::fits::KeywordVector keywords = {
392  daq::fits::ValueKeyword("OBJECT", "OBJECT,SKY"),
393  daq::fits::EsoKeyword("OBS TPLNO", static_cast<uint64_t>(2))};
394  EXPECT_THROW(m_manager.UpdateKeywords("nonexistant-id"sv, keywords), std::invalid_argument);
395 }
396 
397 TEST_F(TestManagerImpl, GetStatus) {
398  // Setup
399  StartDaq1();
400 
401  // Run
402  auto status = m_manager.GetStatus("daq1"sv);
403  EXPECT_EQ(status.id, "daq1");
404  EXPECT_EQ(status.state, daq::State::NotStarted);
405  EXPECT_FALSE(status.error);
406 }
407 
408 TEST_F(TestManagerImpl, GetStatusThrowsIfDaqDoesNotExist) {
409  // Run
410  EXPECT_THROW(m_manager.GetStatus("nonexistant"sv), std::invalid_argument);
411 }
412 
413 TEST_F(TestManagerImpl, AwaitDaqStateReturnsReadyFutureIfConditionIsFulfilled) {
414  // Run
415  StartDaq1();
416  auto fut = m_manager.AwaitDaqStateAsync("daq1"sv, daq::State::NotStarted, 10ms);
417  m_io_ctx.poll();
418 
419  ASSERT_TRUE(fut.is_ready());
420  auto [timeout, result] = fut.get();
421  EXPECT_FALSE(timeout);
422  EXPECT_EQ(result, m_daq1->GetStatus()->GetStatus());
423 }
424 
425 TEST_F(TestManagerImpl, AwaitDaqStateIsReadyWhenConditionIsFulfilled) {
426  // Run
427  StartDaq1();
428  auto fut = m_manager.AwaitDaqStateAsync("daq1"sv, daq::State::Acquiring, 10ms);
429  ASSERT_FALSE(fut.is_ready());
430  m_daq1_status->SetState(daq::State::Acquiring);
431  MakeTestProgress(m_io_ctx, &fut);
432 
433  ASSERT_TRUE(fut.is_ready());
434  auto [timeout, result] = fut.get();
435  EXPECT_FALSE(timeout);
436  EXPECT_EQ(result, m_daq1->GetStatus()->GetStatus());
437 }
438 
439 TEST_F(TestManagerImpl, AwaitDaqStateIsReadyWhenItTimesout) {
440  // Run
441  StartDaq1();
442  auto fut = m_manager.AwaitDaqStateAsync("daq1"sv, daq::State::Acquiring, 0ms);
443  ASSERT_FALSE(fut.is_ready());
444  MakeTestProgress(m_io_ctx, &fut);
445 
446  ASSERT_TRUE(fut.is_ready()) << "Timer should have triggered to make the future ready";
447  auto [timeout, result] = fut.get();
448  EXPECT_TRUE(timeout);
449  EXPECT_EQ(result, m_daq1->GetStatus()->GetStatus());
450 }
451 
452 TEST_F(TestManagerImpl, StoppedDaqTransitionsToNotScheduled) {
453  // Setup
454  StartDaq1();
455 
456  // Test
457  // Simulate completed Acquisition Phase
458  m_daq1_status->SetState(State::Stopped);
459 
460  EXPECT_CALL(*m_daq1, GetEventLog()).WillOnce(Return(m_event_log));
461  EXPECT_CALL(*m_daq1, GetContext()).WillOnce(ReturnRef(m_daq_ctx_1));
462 
463  EXPECT_CALL(*m_dpm_daq1, ScheduleMergeAsync())
464  .WillOnce(Return(ByMove(boost::make_ready_future<State>(State::Scheduled))));
465  // When DAQ transitions to using DpmDaqController workspace is updated
466  EXPECT_CALL(m_workspace, StoreList(_)).Times(AnyNumber());
467  EXPECT_CALL(m_workspace, StoreContext(_)).Times(AnyNumber());
468 
469  // Run pending completion handlers
470  m_io_ctx.poll();
471 
472  // After poll we expect Manager to have created DPM version of daqcontroller and transitioned it
473  // to NotScheduled.
474  EXPECT_EQ(m_daq1_status->GetState(), State::NotScheduled);
475 
476  auto daqs = m_manager.GetDaqControllers();
477  ASSERT_EQ(daqs.size(), 1u);
478  EXPECT_EQ(daqs[0].get(), m_dpm_daq1.get());
479 }
480 
481 TEST_F(TestManagerImpl, RestoreFromWorkspaceAddsDaq) {
482  // Setup
483  // daq1 should create OCM daq controller
484  // daq2 should create DPM daq controller
485  //
486  // Set time to be not stale
487  m_daq_ctx_1.creation_time = std::chrono::system_clock::now();
488  m_daq_ctx_2.creation_time = std::chrono::system_clock::now();
489  m_daq1_status->SetState(State::Acquiring);
490  m_daq2_status->SetState(State::Merging);
491 
492  std::vector<std::string> to_load = {m_daq_ctx_1.id, m_daq_ctx_2.id};
493 
494  EXPECT_CALL(m_workspace, LoadList()).WillOnce(Return(to_load));
495 
496  EXPECT_CALL(m_workspace, LoadContext(m_daq_ctx_1.id)).WillOnce(Return(m_daq_ctx_1));
497  EXPECT_CALL(m_workspace, LoadStatus(m_daq_ctx_1.id))
498  .WillOnce(Return(m_daq1_status->GetStatus()));
499 
500  EXPECT_CALL(m_workspace, LoadContext(m_daq_ctx_2.id)).WillOnce(Return(m_daq_ctx_2));
501  EXPECT_CALL(m_workspace, LoadStatus(m_daq_ctx_2.id))
502  .WillOnce(Return(m_daq2_status->GetStatus()));
503 
504  EXPECT_CALL(m_workspace, StoreList(to_load)).Times(1);
505 
506  // Test
507  m_manager.RestoreFromWorkspace();
508 }
509 
510 TEST_F(TestManagerImpl, RestoreFromWorkspaceArchivesStaleDaqs) {
511  // Setup
512  // daq1 should be archived
513  // daq2 should create DPM daq controller
514  //
515  // Set time to be not stale
516  m_daq_ctx_1.creation_time = std::chrono::system_clock::now() - m_params.acquiring_stale_age;
517  m_daq_ctx_2.creation_time = std::chrono::system_clock::now() - m_params.merging_stale_age;
518 
519  std::vector<std::string> to_load = {m_daq_ctx_1.id, m_daq_ctx_2.id};
520 
521  EXPECT_CALL(m_workspace, LoadList()).WillOnce(Return(to_load));
522 
523  EXPECT_CALL(m_workspace, LoadContext(m_daq_ctx_1.id)).WillOnce(Return(m_daq_ctx_1));
524  EXPECT_CALL(m_workspace, LoadStatus(m_daq_ctx_1.id))
525  .WillOnce(Return(m_daq1_status->GetStatus()));
526  EXPECT_CALL(m_workspace, ArchiveDaq(m_daq_ctx_1.id));
527 
528  EXPECT_CALL(m_workspace, LoadContext(m_daq_ctx_2.id)).WillOnce(Return(m_daq_ctx_2));
529  EXPECT_CALL(m_workspace, LoadStatus(m_daq_ctx_2.id))
530  .WillOnce(Return(m_daq2_dpm_status->GetStatus()));
531  EXPECT_CALL(m_workspace, ArchiveDaq(m_daq_ctx_2.id));
532 
533  EXPECT_CALL(m_workspace, StoreList(std::vector<std::string>())).Times(1);
534 
535  // Test
536  m_manager.RestoreFromWorkspace();
537 }
538 
539 TEST_F(TestManagerImpl, RestoreFromWorkspaceSkipsMissingDaq) {
540  // Setup
541  // daq1 should be archived
542  // daq2 should create DPM daq controller
543  //
544  // Set time to be not stale
545  m_daq_ctx_1.creation_time = std::chrono::system_clock::now();
546  m_daq_ctx_2.creation_time = std::chrono::system_clock::now();
547 
548  std::vector<std::string> to_load = {m_daq_ctx_1.id, m_daq_ctx_2.id};
549  std::vector<std::string> to_store = {m_daq_ctx_2.id};
550 
551  EXPECT_CALL(m_workspace, LoadList()).WillOnce(Return(to_load));
552 
553  EXPECT_CALL(m_workspace, LoadContext(m_daq_ctx_1.id))
554  .WillOnce(Throw(std::runtime_error("ouch")));
555  EXPECT_CALL(m_workspace, LoadStatus(m_daq_ctx_1.id))
556  .Times(Between(0, 1))
557  .WillRepeatedly(Return(m_daq1_status->GetStatus()));
558  EXPECT_CALL(m_workspace, ArchiveDaq(m_daq_ctx_1.id));
559 
560  EXPECT_CALL(m_workspace, LoadContext(m_daq_ctx_2.id)).WillOnce(Return(m_daq_ctx_2));
561  EXPECT_CALL(m_workspace, LoadStatus(m_daq_ctx_2.id))
562  .WillOnce(Return(m_daq2_dpm_status->GetStatus()));
563 
564  EXPECT_CALL(m_workspace, StoreList(to_store)).Times(1);
565 
566  // Test
567  m_manager.RestoreFromWorkspace();
568 }
569 
570 TEST_F(TestManagerImpl, RestoreFromWorkspaceIgnoresArchiveFailure) {
571  // Setup
572  // daq1 should be archived
573  // daq2 should create DPM daq controller
574  //
575  // Set time to be not stale
576  m_daq_ctx_1.creation_time = std::chrono::system_clock::now();
577  m_daq_ctx_2.creation_time = std::chrono::system_clock::now();
578  std::vector<std::string> to_load = {m_daq_ctx_1.id, m_daq_ctx_2.id};
579  std::vector<std::string> to_store = {m_daq_ctx_2.id};
580 
581  EXPECT_CALL(m_workspace, LoadList()).WillOnce(Return(to_load));
582 
583  EXPECT_CALL(m_workspace, LoadContext(m_daq_ctx_1.id))
584  .WillOnce(Throw(std::runtime_error("ouch")));
585  EXPECT_CALL(m_workspace, LoadStatus(m_daq_ctx_1.id))
586  .Times(Between(0, 1))
587  .WillRepeatedly(Return(m_daq1_status->GetStatus()));
588  EXPECT_CALL(m_workspace, ArchiveDaq(m_daq_ctx_1.id))
589  .WillOnce(Throw(std::runtime_error("ouch")));
590 
591  EXPECT_CALL(m_workspace, LoadContext(m_daq_ctx_2.id)).WillOnce(Return(m_daq_ctx_2));
592  EXPECT_CALL(m_workspace, LoadStatus(m_daq_ctx_2.id))
593  .WillOnce(Return(m_daq2_dpm_status->GetStatus()));
594 
595  EXPECT_CALL(m_workspace, StoreList(to_store)).Times(1);
596 
597  // Test
598  m_manager.RestoreFromWorkspace();
599 }
600 
601 TEST_F(TestManagerImpl, ContextSignalUpdatesWorkspace) {
602  // Setup
603  StartDaq1();
604 
605  EXPECT_CALL(m_workspace, StoreContext(Field(&DaqContext::id, "daq1")));
606 
607  // Run
608  m_daq1->signal(m_daq_ctx_1);
609 }
TestManagerImpl::m_daq2_status
std::shared_ptr< ObservableStatus > m_daq2_status
Definition: testManager.cpp:163
TestManagerImplLifecycle::TearDown
void TearDown() override
Definition: testManager.cpp:52
TestManagerImpl::m_daq1
std::shared_ptr< DaqControllerMock > m_daq1
Definition: testManager.cpp:158
daq::ManagerImpl::StartDaqAsync
boost::future< State > StartDaqAsync(DaqContext ctx) override
Start DaqController identified by id.
Definition: manager.cpp:432
TestManagerImpl::m_dpm_daq1
std::shared_ptr< DaqControllerMock > m_dpm_daq1
Definition: testManager.cpp:160
daq::DaqControllerFactoryFake
Factory that creates mock versions.
Definition: daqController.hpp:51
utils.hpp
Defines shared test utilities.
TestManagerImpl::TestManagerImpl
TestManagerImpl()
Definition: testManager.cpp:61
TestManagerImpl::m_daq_ctx_2
DaqContext m_daq_ctx_2
Definition: testManager.cpp:157
TestManagerImpl::m_daq2
std::shared_ptr< DaqControllerMock > m_daq2
Definition: testManager.cpp:159
daq::ManagerImpl
Implements daq::Manager.
Definition: manager.hpp:253
TestManagerImpl::TearDown
void TearDown() override
Definition: testManager.cpp:141
TestManagerImpl::m_daq_id_1
std::string m_daq_id_1
Definition: testManager.cpp:154
rad::IoExecutor
Adapts boost::asio::io_context into a compatible boost::thread Executor type.
Definition: ioExecutor.hpp:12
manager.hpp
Declaration of daq::Manager
daq::DaqContext::id
std::string id
DAQ identfier, possibly provided by user.
Definition: daqContext.hpp:64
daq::State::Transferring
@ Transferring
Input files are being transferred.
daq::MockWorkspace
Definition: mockWorkspace.hpp:18
report.hpp
daq::MakeIdCandidate
std::string MakeIdCandidate(char const *instrument_id, unsigned jitter=0, std::chrono::system_clock::time_point *out=nullptr)
Creates a DAQ id candidate that may or may not be unique.
Definition: manager.cpp:46
daq::fits::EsoKeyword
BasicKeyword< EsoKeywordTraits > EsoKeyword
ESO hiearchical keyword.
Definition: keyword.hpp:337
TestManagerImpl::m_dpm_daq2
std::shared_ptr< DaqControllerMock > m_dpm_daq2
Definition: testManager.cpp:161
daq
Definition: asyncProcess.cpp:15
TestManagerImpl::m_executor
rad::IoExecutor m_executor
Definition: testManager.cpp:151
daq::StatusSignal::ConnectObserver
boost::signals2::connection ConnectObserver(Observer o)
Definition: manager.hpp:82
FixtureBase
Definition: testManager.cpp:26
daq::Status::TimePoint
std::chrono::time_point< std::chrono::steady_clock > TimePoint
Definition: status.hpp:121
daq::DaqControllerFactoryFake::ocm_mocks
std::map< std::string, std::shared_ptr< DaqControllerMock > > ocm_mocks
Definition: daqController.hpp:65
daq::error::NestedExceptionReporter
Adapter object intended to be used in contexts without direct access to the output-stream object.
Definition: report.hpp:54
daq::DaqContext
Structure carrying context needed to start a Data Acquisition and construct a Data Product Specificat...
Definition: daqContext.hpp:48
daq::State::Merging
@ Merging
DAQ is being merged.
TestManagerImpl::SetUp
void SetUp() override
Creates manager and adds two data acquisitions.
Definition: testManager.cpp:70
TestManagerImpl::m_io_ctx
boost::asio::io_context m_io_ctx
Definition: testManager.cpp:150
TestManagerImpl
Definition: testManager.cpp:59
TestManagerImpl::m_daq_id_2
std::string m_daq_id_2
Definition: testManager.cpp:155
daq::TEST_F
TEST_F(TestDpmDaqController, StatusUpdateInNotScheduledSucceeds)
Definition: testDpmDaqController.cpp:60
statusObserver.hpp
TestManagerImpl::m_daq_ctx_1
DaqContext m_daq_ctx_1
Definition: testManager.cpp:156
daqController.hpp
Mock of DaqController.
TestManagerImpl::m_daq1_dpm_status
std::shared_ptr< ObservableStatus > m_daq1_dpm_status
Definition: testManager.cpp:164
daq::Status::id
std::string id
Definition: status.hpp:136
StatusObserverMock
Simple observer used for testing.
Definition: statusObserver.hpp:22
daq::ManagerImpl::AwaitDaqStateAsync
boost::future< Result< Status > > AwaitDaqStateAsync(std::string_view id, State, std::chrono::milliseconds timeout) override
Await DAQ state.
Definition: manager.cpp:480
TestManagerImplLifecycle::SetUp
void SetUp() override
Definition: testManager.cpp:50
TestManagerImpl::m_daq1_status
std::shared_ptr< ObservableStatus > m_daq1_status
Definition: testManager.cpp:162
TestManagerImpl::m_daq2_dpm_status
std::shared_ptr< ObservableStatus > m_daq2_dpm_status
Definition: testManager.cpp:165
daq::DaqContext::file_id
std::string file_id
Data Product FileId as specified by OLAS ICD.
Definition: daqContext.hpp:69
daq::fits::ValueKeyword
BasicKeyword< ValueKeywordTraits > ValueKeyword
Standard FITS value keyword.
Definition: keyword.hpp:330
TestManagerImpl::m_daq_factory
DaqControllerFactoryFake m_daq_factory
Definition: testManager.cpp:166
daq::Status
Non observable status object that keeps stores status of data acquisition.
Definition: status.hpp:120
daq::fits::KeywordVector
std::vector< KeywordVariant > KeywordVector
Vector of keywords.
Definition: keyword.hpp:414
daq::ManagerImpl::GetStatusSignal
StatusSignal & GetStatusSignal() override
Definition: manager.cpp:522
daq::ManagerParams
Configurations parameters directly related to manager.
Definition: manager.hpp:35
daq::DaqOperationAborted
Started operation was aborted.
Definition: error.hpp:47
TestManagerImpl::m_event_log
std::shared_ptr< daq::ObservableEventLog > m_event_log
Definition: testManager.cpp:153
TestManagerImplLifecycle
Definition: testManager.cpp:48
TestManagerImpl::m_workspace
MockWorkspace m_workspace
Definition: testManager.cpp:152
daq::UpdateKeywords
void UpdateKeywords(DaqContext &ctx, fits::KeywordVector const &keywords)
Updates (adds or replaces) primary HDU keywords.
Definition: daqContext.cpp:28
daq::ErrorPolicy::Strict
@ Strict
Any error is considered fatal and may lead to the operation being aborted.
daq::ObservableEventLog
Stores data acquisition status and allows subscription to status changes.
Definition: eventLog.hpp:107
TestManagerImpl::m_manager
ManagerImpl m_manager
Definition: testManager.cpp:167
TestManagerImpl::StartDaq1
auto StartDaq1() -> boost::future< State >
Definition: testManager.cpp:132
FixtureBase::ExpectNoException
T ExpectNoException(boost::future< T > &f)
Definition: testManager.cpp:28
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