10 #include <fmt/format.h>
11 #include <gtest/gtest.h>
19 #include "mock/daqifMock.hpp"
22 using namespace ::testing;
23 using namespace std::literals;
39 , m_event_log(std::make_shared<
daq::ObservableEventLog>())
41 m_io_ctx, m_mal_mock, m_mgr_mock,
"ocm",
"/tmp", m_event_log)) {
44 m_metadata_sources =
"meta@zpb.rr://uri";
45 m_prim_sources =
"prim@zpb.rr://uri";
46 m_daq_properties = R
"(
55 "type": "valueKeyword",
67 "type": "metadataSource",
97 TEST(TestParseSingleSource, Successful) {
104 TEST(TestParseSingleSource, LeadingOrTrailingSpacesAreAllowed) {
111 TEST(TestParseSingleSource, Incomplete) {
114 std::invalid_argument);
116 std::invalid_argument);
117 EXPECT_THROW(
ParseSourceUri(fmt::format(
"@,")), std::invalid_argument);
119 std::invalid_argument);
121 std::invalid_argument);
124 TEST(TestParseMultipleSources, Successful) {
127 std::vector<ParsedSource> result{expected1, expected2};
144 TEST(TestParseSingleSource, Empty) {
147 std::invalid_argument);
148 EXPECT_THROW(
ParseSourceUri(fmt::format(
"{}@,{} ", expected.
name,
"")), std::invalid_argument);
150 std::invalid_argument);
153 TEST(TestParseDaqContext, Successful) {
154 auto max_err_ms = 0.001;
156 auto properties_str = R
"(
160 "type": "esoKeyword",
165 "type": "valueKeyword",
174 EXPECT_THAT(
static_cast<double>(properties.await_interval.count()),
175 DoubleNear(100, max_err_ms));
178 auto properties_str = R
"(
184 EXPECT_THAT(
static_cast<double>(properties.await_interval.count()),
185 DoubleNear(1000, max_err_ms));
189 TEST(TestParseDaqContext, Failures) {
197 EXPECT_CALL(m_mgr_mock, HaveDaq(
"id"sv, _)).WillOnce(Return(
true));
199 EXPECT_CALL(m_mgr_mock, MakeDaqId(_)).Times(Between(0, 1)).WillOnce(Return(
"file-id"));
202 auto fut = m_daq_impl->StartDaq(
"id",
"prefix", m_prim_sources, m_metadata_sources,
"");
204 ASSERT_TRUE(fut.is_ready()) <<
"future should be ready by now since daq with id already exist";
205 EXPECT_THROW(fut.get(), daqif::DaqException);
212 EXPECT_CALL(m_mgr_mock, MakeDaqId(_)).WillOnce(Return(
"id"));
216 EXPECT_CALL(m_mal_mock, getDataEntityUnsafe(_,
"daqif_DaqReply"))
217 .WillOnce(Return(fake_reply))
218 .RetiresOnSaturation();
221 auto fut = m_daq_impl->StartDaq(
"",
"prefix", m_prim_sources, m_metadata_sources,
"");
225 ASSERT_TRUE(fut.is_ready()) <<
"future should be ready immediately since we faked a "
226 "synchronous start with make_ready_future";
227 EXPECT_TRUE(fut.has_value());
228 auto reply = fut.get();
236 EXPECT_CALL(m_mgr_mock, HaveDaq(
"id"sv, _)).WillOnce(Return(
true));
238 EXPECT_CALL(m_mgr_mock, MakeDaqId(_)).Times(Between(0, 1)).WillOnce(Return(
"file-id"));
241 auto fut = m_daq_impl->StartDaqV2(m_daqv2_spec);
243 ASSERT_TRUE(fut.is_ready()) <<
"future should be ready by now since daq with id already exist";
244 EXPECT_THROW(fut.get(), daqif::DaqException);
251 auto fut = m_daq_impl->StartDaq(
"id",
"prefix",
"", m_metadata_sources,
"INVALID JSON");
253 ASSERT_TRUE(fut.is_ready()) <<
"future should be ready by now since daq with id already exist";
254 EXPECT_THROW(fut.get(), daqif::DaqException);
262 auto fut = m_daq_impl->StartDaq(
"id",
"prefix",
"", m_metadata_sources,
"[]");
264 ASSERT_TRUE(fut.is_ready());
265 EXPECT_THROW(fut.get(), daqif::DaqException);
273 auto fut = m_daq_impl->StartDaq(
"id",
"/path/prefix",
"", m_metadata_sources, m_daq_properties);
275 ASSERT_TRUE(fut.is_ready());
276 EXPECT_THROW(fut.get(), daqif::DaqException);
283 EXPECT_CALL(m_mgr_mock, MakeDaqId(_)).Times(Between(0, 1)).WillOnce(Return(
"file-id"));
284 EXPECT_CALL(m_mgr_mock, HaveDaq(
"id"sv, _)).WillOnce(Return(
false));
288 EXPECT_CALL(m_mal_mock, getDataEntityUnsafe(_,
"daqif_DaqReply"))
289 .WillOnce(Return(fake_reply))
290 .RetiresOnSaturation();
293 auto fut = m_daq_impl->StartDaq(
"id",
"prefix",
"", m_metadata_sources, m_daq_properties);
298 ASSERT_TRUE(fut.is_ready()) <<
"future should be ready immediately since we faked a "
299 "synchronous start with make_ready_future";
300 EXPECT_TRUE(fut.has_value());
301 auto reply = fut.get();
308 EXPECT_CALL(m_mgr_mock, MakeDaqId(_)).Times(Between(0, 1)).WillOnce(Return(
"file-id"));
309 EXPECT_CALL(m_mgr_mock, HaveDaq(
"id"sv, _)).WillOnce(Return(
false));
310 EXPECT_CALL(m_mgr_mock, StartDaqAsync(_)).WillOnce(Throw(std::runtime_error(
"error")));
313 auto fut = m_daq_impl->StartDaq(
"id",
"prefix",
"", m_metadata_sources,
"");
315 ASSERT_TRUE(fut.is_ready()) <<
"future should be ready immediately since we faked a "
316 "synchronous start with make_ready_future";
317 EXPECT_THROW(fut.get(), daqif::DaqException)
318 <<
"future should have contained the ICD exception type";
324 EXPECT_CALL(m_mgr_mock, MakeDaqId(_)).WillOnce(Return(
"fileid"));
325 EXPECT_CALL(m_mgr_mock, HaveDaq(
"id"sv, _)).WillOnce(Return(
false));
327 .WillOnce(Return(ByMove(boost::make_exceptional_future<daq::State>(
329 EXPECT_CALL(m_mgr_mock, GetStatus(
"id"sv)).WillOnce(Return(
daq::Status()));
332 auto fut = m_daq_impl->StartDaq(
"id",
"prefix",
"", m_metadata_sources,
"");
334 ASSERT_TRUE(fut.is_ready())
335 <<
"future should be ready now since we faked a synchronous start with make_ready_future";
336 EXPECT_THROW(fut.get(), daqif::DaqException)
337 <<
"future should have contained the ICD exception type";
344 EXPECT_CALL(m_mgr_mock, MakeDaqId(_)).WillOnce(Return(
"id"));
346 EXPECT_CALL(m_mgr_mock, HaveDaq(
"id"sv, _)).WillOnce(Return(
false));
350 EXPECT_CALL(m_mal_mock, getDataEntityUnsafe(_,
"daqif_DaqReply"))
351 .WillOnce(Return(fake_reply))
352 .RetiresOnSaturation();
356 auto fut = m_daq_impl->StartDaqV2(R
"(
360 "type": "metadataSource",
361 "sourceName": "name",
370 ASSERT_TRUE(fut.is_ready()) <<
"future should be ready immediately since we faked a "
371 "synchronous start with make_ready_future";
373 EXPECT_TRUE(fut.has_value());
374 auto reply = fut.get();
377 }
catch (daqif::DaqException
const& e) {
378 FAIL() << e.getMessage();
379 }
catch (std::exception
const& e) {
388 auto fut = m_daq_impl->StartDaqV2(
",]{");
390 ASSERT_TRUE(fut.is_ready()) <<
"future should be ready now as parsing failed";
391 EXPECT_THROW(fut.get(), daqif::DaqException)
392 <<
"future should have contained the ICD exception type";
399 auto fut = m_daq_impl->StartDaqV2(
"{}");
401 ASSERT_TRUE(fut.is_ready()) <<
"future should be ready now as schema validation failed";
402 EXPECT_THROW(fut.get(), daqif::DaqException)
403 <<
"future should have contained the ICD exception type";
411 EXPECT_CALL(m_mgr_mock, MakeDaqId(_)).WillOnce(Return(
"file-id"));
412 EXPECT_CALL(m_mgr_mock, HaveDaq(
"id"sv, _)).WillOnce(Return(
false));
413 EXPECT_CALL(m_mgr_mock,
418 EXPECT_CALL(m_mal_mock, getDataEntityUnsafe(_,
"daqif_DaqReply"))
419 .WillOnce(Return(fake_reply))
420 .RetiresOnSaturation();
423 auto fut = m_daq_impl->StartDaqV2(m_daqv2_spec);
428 ASSERT_TRUE(fut.is_ready()) <<
"future should be ready immediately since we faked a "
429 "synchronous start with make_ready_future";
430 EXPECT_TRUE(fut.has_value());
431 auto reply = fut.get();
439 EXPECT_CALL(m_mgr_mock, HaveDaq(
"id"sv, _)).Times(Between(0, 1)).WillRepeatedly(Return(
false));
442 .Times(Between(0, 1))
443 .WillOnce(Return(ByMove(
444 boost::make_exceptional_future<daq::Status>(std::invalid_argument(
"no such id")))));
446 EXPECT_CALL(m_mgr_mock, GetStatus(
"id"sv)).WillOnce(Throw(std::invalid_argument(
"no such id")));
449 auto reply_fut = m_daq_impl->StopDaq(
"id");
451 ASSERT_TRUE(reply_fut.is_ready());
452 EXPECT_THROW(reply_fut.get(), daqif::DaqException);
461 EXPECT_CALL(m_mgr_mock, HaveDaq(
"id"sv, _)).Times(Between(0, 1)).WillRepeatedly(Return(
true));
463 .WillOnce(Return(ByMove(boost::make_ready_future<daq::Status>(status))));
464 EXPECT_CALL(m_mal_mock, getDataEntityUnsafe(_,
"daqif_DaqReply"))
465 .WillOnce(Return(fake_reply))
466 .RetiresOnSaturation();
469 auto reply_fut = m_daq_impl->StopDaq(
"id");
471 ASSERT_TRUE(reply_fut.is_ready());
472 std::shared_ptr<daqif::DaqReply> reply = reply_fut.get();
482 EXPECT_CALL(m_mgr_mock, HaveDaq(
"id"sv, _)).Times(Between(0, 1)).WillRepeatedly(Return(
true));
484 .WillOnce(Return(ByMove(boost::make_ready_future<daq::Status>(status))));
485 EXPECT_CALL(m_mal_mock, getDataEntityUnsafe(_,
"daqif_DaqReply"))
486 .WillOnce(Return(fake_reply))
487 .RetiresOnSaturation();
490 auto reply_fut = m_daq_impl->ForceStopDaq(
"id");
492 ASSERT_TRUE(reply_fut.is_ready());
493 std::shared_ptr<daqif::DaqReply> reply = reply_fut.get();
500 EXPECT_CALL(m_mgr_mock, HaveDaq(
"id"sv, _)).Times(Between(0, 1)).WillRepeatedly(Return(
false));
502 .Times(Between(0, 1))
503 .WillOnce(Return(ByMove(
504 boost::make_exceptional_future<daq::Status>(std::invalid_argument(
"no such id")))));
506 EXPECT_CALL(m_mgr_mock, GetStatus(
"id"sv)).WillOnce(Throw(std::invalid_argument(
"no such id")));
509 auto reply_fut = m_daq_impl->AbortDaq(
"id");
511 ASSERT_TRUE(reply_fut.is_ready());
512 EXPECT_THROW(reply_fut.get(), daqif::DaqException);
523 EXPECT_CALL(m_mgr_mock, HaveDaq(
"id"sv, _)).Times(Between(0, 1)).WillRepeatedly(Return(
true));
525 .WillOnce(Return(ByMove(boost::make_ready_future<daq::Status>(reply_status))));
526 EXPECT_CALL(m_mal_mock, getDataEntityUnsafe(_,
"daqif_DaqReply"))
527 .WillOnce(Return(fake_reply))
528 .RetiresOnSaturation();
531 auto reply_fut = m_daq_impl->AbortDaq(
"id");
533 ASSERT_TRUE(reply_fut.is_ready());
534 std::shared_ptr<daqif::DaqReply> reply = reply_fut.get();
545 EXPECT_CALL(m_mgr_mock, HaveDaq(
"id"sv, _)).Times(Between(0, 1)).WillRepeatedly(Return(
true));
547 .WillOnce(Return(ByMove(boost::make_ready_future<daq::Status>(reply_status))));
548 EXPECT_CALL(m_mal_mock, getDataEntityUnsafe(_,
"daqif_DaqReply"))
549 .WillOnce(Return(fake_reply))
550 .RetiresOnSaturation();
553 auto reply_fut = m_daq_impl->ForceAbortDaq(
"id");
555 ASSERT_TRUE(reply_fut.is_ready());
556 std::shared_ptr<daqif::DaqReply> reply = reply_fut.get();
562 std::string keywords = R
"(
565 "type":"valueKeyword",
582 EXPECT_CALL(m_mgr_mock, HaveDaq(
"id"sv, _)).Times(Between(0, 1)).WillRepeatedly(Return(
true));
584 EXPECT_CALL(m_mal_mock, getDataEntityUnsafe(_,
"daqif_DaqReply"))
585 .WillOnce(Return(fake_reply))
586 .RetiresOnSaturation();
589 auto reply_fut = m_daq_impl->UpdateKeywords(
"id", keywords);
591 ASSERT_TRUE(reply_fut.is_ready());
592 std::shared_ptr<daqif::DaqReply> reply = reply_fut.get();
599 std::string keywords_with_trailing_comma = R
"(
602 "type":"valueKeyword",
614 EXPECT_CALL(m_mgr_mock, HaveDaq(
"id"sv, _)).Times(Between(0, 1)).WillRepeatedly(Return(
true));
618 auto reply_fut = m_daq_impl->UpdateKeywords(
"id", keywords_with_trailing_comma);
620 ASSERT_TRUE(reply_fut.is_ready());
621 EXPECT_THROW(reply_fut.get(), daqif::DaqException);
626 std::string keywords_with_unknown_type = R
"(
629 "type":"unknownKeywordHere",
636 EXPECT_CALL(m_mgr_mock, HaveDaq(
"id"sv, _)).Times(Between(0, 1)).WillRepeatedly(Return(
true));
640 auto reply_fut = m_daq_impl->UpdateKeywords(
"id", keywords_with_unknown_type);
642 ASSERT_TRUE(reply_fut.is_ready());
643 EXPECT_THROW(reply_fut.get(), daqif::DaqException);
648 EXPECT_CALL(m_mgr_mock, HaveDaq(
"id"sv, _)).Times(Between(0, 1)).WillRepeatedly(Return(
false));
649 EXPECT_CALL(m_mgr_mock, GetStatus(
"id"sv))
650 .Times(Between(0, 1))
651 .WillOnce(Throw(std::invalid_argument(
"no such id")));
654 auto reply_fut = m_daq_impl->GetStatus(
"id");
655 EXPECT_FALSE(reply_fut.is_ready())
656 <<
"future cannot be ready since implementation should use provided executor to provide "
659 ASSERT_TRUE(reply_fut.is_ready());
660 EXPECT_THROW(reply_fut.get(), daqif::DaqException);
668 EXPECT_CALL(m_mgr_mock, HaveDaq(
"id"sv, _)).Times(Between(0, 1)).WillRepeatedly(Return(
true));
669 EXPECT_CALL(m_mgr_mock, GetStatus(
"id"sv)).Times(Between(0, 1)).WillOnce(Return(status));
670 EXPECT_CALL(m_mal_mock, getDataEntityUnsafe(_,
"daqif_DaqStatus"))
671 .WillOnce(Return(fake_reply))
672 .RetiresOnSaturation();
675 auto reply_fut = m_daq_impl->GetStatus(
"id");
676 EXPECT_FALSE(reply_fut.is_ready())
677 <<
"future cannot be ready since implementation should use provided executor to provide "
680 ASSERT_TRUE(reply_fut.is_ready());
681 std::shared_ptr<daqif::DaqStatus> reply = reply_fut.get();
685 EXPECT_EQ(reply->getState(), daqif::StateAcquiring);
686 EXPECT_EQ(reply->getSubState(), daqif::Acquiring);
691 auto status1 = std::make_shared<daq::ObservableStatus>(
"completed",
"fileid1");
692 auto status2 = std::make_shared<daq::ObservableStatus>(
"active1",
"fileid2");
693 auto status3 = std::make_shared<daq::ObservableStatus>(
"active2",
"fileid3");
694 auto status4 = std::make_shared<daq::ObservableStatus>(
"completed",
"fileid4");
699 auto daq1 = std::make_shared<DaqControllerFake>(status1);
700 auto daq2 = std::make_shared<DaqControllerFake>(status2);
701 auto daq3 = std::make_shared<DaqControllerFake>(status3);
702 auto daq4 = std::make_shared<DaqControllerFake>(status4);
703 std::vector<std::shared_ptr<daq::DaqController const>> daqs;
704 daqs.push_back(daq1);
705 daqs.push_back(daq2);
706 daqs.push_back(daq3);
707 daqs.push_back(daq4);
712 EXPECT_CALL(m_mal_mock, getDataEntityUnsafe(_,
"daqif_DaqStatus"))
713 .WillOnce(Return(st1))
714 .WillOnce(Return(st2))
715 .RetiresOnSaturation();
717 EXPECT_CALL(m_mgr_mock, GetDaqControllers()).WillOnce(Return(daqs));
720 auto reply_fut = m_daq_impl->GetActiveList();
722 ASSERT_TRUE(reply_fut.is_ready());
723 auto reply = reply_fut.get();
731 EXPECT_CALL(m_mgr_mock, GetStatus(
"id"sv)).WillRepeatedly(Return(
daq::Status()));
736 auto fut = m_daq_impl->AwaitDaqState(
"id", daqif::StateAcquiring, daqif::Acquiring, 0.0);
738 ASSERT_TRUE(fut.is_ready());
739 EXPECT_THROW(fut.get(), daqif::DaqException);
744 auto fut = m_daq_impl->AwaitDaqState(
"id", daqif::StateMerging, daqif::Acquiring, 1.0);
746 ASSERT_TRUE(fut.is_ready());
747 EXPECT_THROW(fut.get(), daqif::DaqException);
756 boost::promise<daq::Result<daq::Status>> promise;
758 .WillOnce(Return(ByMove(promise.get_future())));
762 EXPECT_CALL(m_mal_mock, getDataEntityUnsafe(_,
"daqif_AwaitDaqReply"))
763 .WillOnce(Return(reply))
764 .RetiresOnSaturation();
767 auto fut = m_daq_impl->AwaitDaqState(
"id", daqif::StateAcquiring, daqif::Acquiring, 1.0);
768 ASSERT_FALSE(fut.is_ready());
769 promise.set_value({
false, status});
772 auto val = fut.get();
773 EXPECT_FALSE(val->getTimeout());
774 auto status = val->getStatus();
777 EXPECT_EQ(status->getState(), daqif::StateAcquiring);
778 EXPECT_EQ(status->getSubState(), daqif::Acquiring);
786 boost::promise<daq::State> mgr_promise;
787 EXPECT_CALL(m_mgr_mock, HaveDaq(
"id"sv, _)).WillOnce(Return(
false));
788 EXPECT_CALL(m_mgr_mock, MakeDaqId(_)).WillOnce(Return(
"fileid"));
789 EXPECT_CALL(m_mgr_mock, StartDaqAsync(_)).WillOnce(Return(ByMove(mgr_promise.get_future())));
792 auto reply_fut = m_daq_impl->StartDaq(
"id",
"prefix", m_prim_sources, m_metadata_sources,
"");
801 ASSERT_TRUE(reply_fut.is_ready());
802 EXPECT_THROW(reply_fut.get(), daqif::DaqException);
809 boost::promise<daq::Status> mgr_promise;
812 EXPECT_CALL(m_mgr_mock, HaveDaq(
"id"sv, _)).Times(Between(0, 1)).WillRepeatedly(Return(
true));
814 .WillOnce(Return(ByMove(mgr_promise.get_future())));
817 auto reply_fut = m_daq_impl->StopDaq(
"id");
822 mgr_promise.set_value(
827 ASSERT_TRUE(reply_fut.is_ready());
828 EXPECT_THROW(reply_fut.get(), daqif::DaqException);
835 boost::promise<daq::Status> mgr_promise;
841 EXPECT_CALL(m_mgr_mock, HaveDaq(
"id"sv, _)).Times(Between(0, 1)).WillRepeatedly(Return(
true));
843 .WillOnce(Return(ByMove(mgr_promise.get_future())));
846 auto reply_fut = m_daq_impl->AbortDaq(
"id");
851 mgr_promise.set_value(reply_status);
855 ASSERT_TRUE(reply_fut.is_ready());
856 EXPECT_THROW(reply_fut.get(), daqif::DaqException);
Represents error in single source.
Exception thrown to carry reply errors.
Contains error related declarations for DAQ.
Implements the MAL interface daqif::OcmDaq (async version).
std::vector< KeywordVariant > KeywordVector
Vector of keywords.
BasicKeyword< ValueKeywordTraits > ValueKeyword
Standard FITS value keyword.
BasicKeyword< EsoKeywordTraits > EsoKeyword
ESO hiearchical keyword.
void UpdateKeywords(DaqContext &ctx, fits::KeywordVector const &keywords)
Updates (adds or replaces) primary HDU keywords.
@ 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 ...
@ Aborted
Data acquisition has been aborted by user.
@ Acquiring
All data sources have reported data acquisition is in progress.
@ Stopped
All data sources have reported they have stopped acquiring data.
daq::DaqContext ParseStartDaqContext(std::string const &json_properties)
Parse the JSON properties user provides with StartDaq.
ParsedSource ParseSourceUri(std::string_view s)
Parse user provided string in the format "<name>@<rr-uri>".
std::vector< ParsedSource > ParseSourceUris(std::string_view s)
Parse user provided string in the format "<name>@<rr-uri>[ <name>@...]".
Declaration of OcmDaqService.
Mockup of metadaqif classes.
Fixture for testing when OcmDaqService is abandoned (e.g.
Fixture that sets up a OcmDaqService instance with following mockups:
std::shared_ptr< daq::ObservableEventLog > m_event_log
std::string m_metadata_sources
boost::asio::io_context m_io_ctx
std::shared_ptr< OcmDaqService > m_daq_impl
std::string m_daq_properties
std::string m_prim_sources
std::string file_id
Data Product FileId as specified by OLAS ICD.
std::string id
DAQ identfier, possibly provided by user.
Non observable status object that keeps stores status of data acquisition.
std::chrono::time_point< std::chrono::steady_clock > TimePoint
TEST_F(TestOcmDaqService, StartDaqShouldTestIfIdAlreadyExistsAndReturnFailureIfItDoes)
TEST(TestParseSingleSource, Successful)
EXPECT_EQ(meta.rr_uri, "zpb.rr://meta")