9 #include <fmt/ostream.h> 
   13 #include "mock/mockWorkspace.hpp" 
   16 #include <gtest/gtest.h> 
   17 #include <log4cplus/loggingmacros.h> 
   22 using namespace ::testing;
 
   23 using namespace std::literals::string_view_literals;
 
   24 using namespace std::chrono_literals;
 
   30             ADD_FAILURE() << 
"Future is not ready";
 
   31             throw std::runtime_error(
"test failure");
 
   36             LOG4CPLUS_ERROR(
"test",
 
   37                             "Future contained exception\n" 
   63         , m_executor(m_io_ctx)
 
   65         , m_manager(m_executor, m_params, m_workspace, m_event_log, m_daq_factory) {
 
   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";
 
   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;
 
   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;
 
   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())
 
   96             .WillRepeatedly(Return(m_daq1_status));
 
   97         EXPECT_CALL(Const(*m_daq2), GetStatus())
 
   99             .WillRepeatedly(Return(m_daq2_status));
 
  100         EXPECT_CALL(Const(*m_daq1), GetContext())
 
  102             .WillRepeatedly(ReturnRef(m_daq_ctx_1));
 
  104         m_daq1_dpm_status = std::make_shared<ObservableStatus>(m_daq_id_1, 
"fileid1");
 
  106         m_daq2_dpm_status = std::make_shared<ObservableStatus>(m_daq_id_2, 
"fileid2");
 
  109         EXPECT_CALL(*m_dpm_daq1, GetId()).WillRepeatedly(ReturnRef(m_daq_id_1));
 
  110         EXPECT_CALL(*m_dpm_daq1, GetStatus())
 
  112             .WillRepeatedly(Return(m_daq1_status));
 
  113         EXPECT_CALL(Const(*m_dpm_daq1), GetStatus())
 
  115             .WillRepeatedly(Return(m_daq1_status));
 
  116         EXPECT_CALL(Const(*m_dpm_daq1), GetContext())
 
  118             .WillRepeatedly(ReturnRef(m_daq_ctx_1));
 
  120         EXPECT_CALL(*m_dpm_daq2, GetId()).WillRepeatedly(ReturnRef(m_daq_id_2));
 
  121         EXPECT_CALL(*m_dpm_daq2, GetStatus())
 
  123             .WillRepeatedly(Return(m_daq2_dpm_status));
 
  124         EXPECT_CALL(Const(*m_dpm_daq2), GetStatus())
 
  126             .WillRepeatedly(Return(m_daq2_dpm_status));
 
  127         EXPECT_CALL(Const(*m_dpm_daq2), GetContext())
 
  129             .WillRepeatedly(ReturnRef(m_daq_ctx_2));
 
  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);
 
  146         m_daq_factory.ocm_mocks.clear();
 
  147         m_daq_factory.dpm_mocks.clear();
 
  158     std::shared_ptr<DaqControllerMock> 
m_daq1;
 
  159     std::shared_ptr<DaqControllerMock> 
m_daq2;
 
  172     boost::asio::io_context io_ctx;
 
  175     auto event_log = std::make_shared<ObservableEventLog>();
 
  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");
 
  184     auto daq = std::make_shared<DaqControllerMock>();
 
  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));
 
  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));
 
  199     EXPECT_CALL(o, CallOperator(_));
 
  205     ExpectNoException(f);
 
  210     boost::asio::io_context io_ctx;
 
  213     auto event_log = std::make_shared<ObservableEventLog>();
 
  217     auto id = std::string(
"id");
 
  220     auto status = std::make_shared<ObservableStatus>(
id, 
"fileid");
 
  221     auto daq = std::make_shared<DaqControllerMock>();
 
  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));
 
  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));
 
  234     boost::future<Result<Status>> res;
 
  236         ManagerImpl mgr(executor, m_params, workspace, event_log, factory);
 
  241         ASSERT_FALSE(res.is_ready());
 
  246     ASSERT_TRUE(res.is_ready());
 
  252         std::regex regex(
"INSTR\\.\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}");
 
  254         EXPECT_TRUE(std::regex_match(
id, regex))
 
  255             << 
"Instrument ID should be truncated to 5 characters if too long";
 
  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));
 
  265         EXPECT_NE(id1, id2) << 
"Adding jitter should have made the ID different";
 
  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(_));
 
  277     m_manager.StartDaqAsync(m_daq_ctx_1);
 
  280     auto f = m_manager.StartDaqAsync(m_daq_ctx_1);
 
  282     ASSERT_TRUE(f.is_ready());
 
  283     EXPECT_THROW(f.get(), std::invalid_argument);
 
  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(_));
 
  295     auto fut = m_manager.StartDaqAsync(m_daq_ctx_1);
 
  297     ASSERT_TRUE(fut.is_ready());
 
  298     EXPECT_EQ(fut.get(), State::Starting);
 
  310     EXPECT_CALL(*m_daq1, StartAsync())
 
  312             ByMove(boost::make_exceptional_future<State>(std::runtime_error(
"START FAILED")))));
 
  313     boost::promise<daq::Status> abort_reply;
 
  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(_));
 
  321     auto f = m_manager.StartDaqAsync(m_daq_ctx_1);
 
  323     ASSERT_FALSE(f.is_ready());
 
  326     abort_reply.set_exception(std::logic_error(
"ABORT ALSO FAILED"));
 
  329     ASSERT_TRUE(f.is_ready());
 
  330     EXPECT_THROW(f.get(), std::runtime_error);
 
  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);
 
  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)))));
 
  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);
 
  357     ASSERT_TRUE(fut.is_ready());
 
  358     EXPECT_THROW(fut.get(), std::invalid_argument);
 
  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))));
 
  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);
 
  387     m_manager.UpdateKeywords(
"daq1"sv, keywords);
 
  394     EXPECT_THROW(m_manager.UpdateKeywords(
"nonexistant-id"sv, keywords), std::invalid_argument);
 
  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);
 
  410     EXPECT_THROW(m_manager.GetStatus(
"nonexistant"sv), std::invalid_argument);
 
  416     auto fut = m_manager.AwaitDaqStateAsync(
"daq1"sv, daq::State::NotStarted, 10ms);
 
  419     ASSERT_TRUE(fut.is_ready());
 
  420     auto [timeout, result] = fut.get();
 
  421     EXPECT_FALSE(timeout);
 
  422     EXPECT_EQ(result, m_daq1->GetStatus()->GetStatus());
 
  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);
 
  433     ASSERT_TRUE(fut.is_ready());
 
  434     auto [timeout, result] = fut.get();
 
  435     EXPECT_FALSE(timeout);
 
  436     EXPECT_EQ(result, m_daq1->GetStatus()->GetStatus());
 
  442     auto fut = m_manager.AwaitDaqStateAsync(
"daq1"sv, daq::State::Acquiring, 0ms);
 
  443     ASSERT_FALSE(fut.is_ready());
 
  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());
 
  458     m_daq1_status->SetState(State::Stopped);
 
  460     EXPECT_CALL(*m_daq1, GetEventLog()).WillOnce(Return(m_event_log));
 
  461     EXPECT_CALL(*m_daq1, GetContext()).WillOnce(ReturnRef(m_daq_ctx_1));
 
  463     EXPECT_CALL(*m_dpm_daq1, ScheduleMergeAsync())
 
  464         .WillOnce(Return(ByMove(boost::make_ready_future<State>(State::Scheduled))));
 
  466     EXPECT_CALL(m_workspace, StoreList(_)).Times(AnyNumber());
 
  467     EXPECT_CALL(m_workspace, StoreContext(_)).Times(AnyNumber());
 
  474     EXPECT_EQ(m_daq1_status->GetState(), State::NotScheduled);
 
  476     auto daqs = m_manager.GetDaqControllers();
 
  477     ASSERT_EQ(daqs.size(), 1u);
 
  478     EXPECT_EQ(daqs[0].get(), m_dpm_daq1.get());
 
  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);
 
  492     std::vector<std::string> to_load = {m_daq_ctx_1.id, m_daq_ctx_2.id};
 
  494     EXPECT_CALL(m_workspace, LoadList()).WillOnce(Return(to_load));
 
  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()));
 
  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()));
 
  504     EXPECT_CALL(m_workspace, StoreList(to_load)).Times(1);
 
  507     m_manager.RestoreFromWorkspace();
 
  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;
 
  519     std::vector<std::string> to_load = {m_daq_ctx_1.id, m_daq_ctx_2.id};
 
  521     EXPECT_CALL(m_workspace, LoadList()).WillOnce(Return(to_load));
 
  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));
 
  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));
 
  533     EXPECT_CALL(m_workspace, StoreList(std::vector<std::string>())).Times(1);
 
  536     m_manager.RestoreFromWorkspace();
 
  545     m_daq_ctx_1.creation_time = std::chrono::system_clock::now();
 
  546     m_daq_ctx_2.creation_time = std::chrono::system_clock::now();
 
  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};
 
  551     EXPECT_CALL(m_workspace, LoadList()).WillOnce(Return(to_load));
 
  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));
 
  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()));
 
  564     EXPECT_CALL(m_workspace, StoreList(to_store)).Times(1);
 
  567     m_manager.RestoreFromWorkspace();
 
  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};
 
  581     EXPECT_CALL(m_workspace, LoadList()).WillOnce(Return(to_load));
 
  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")));
 
  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()));
 
  595     EXPECT_CALL(m_workspace, StoreList(to_store)).Times(1);
 
  598     m_manager.RestoreFromWorkspace();
 
  605     EXPECT_CALL(m_workspace, StoreContext(Field(&
DaqContext::id, 
"daq1")));
 
  608     m_daq1->signal(m_daq_ctx_1);