LCOV - code coverage report
Current view: top level - dfmodules/unittest - HDF5Write_test.cxx (source / functions) Coverage Total Hit
Test: code.result Lines: 100.0 % 191 191
Test Date: 2025-12-21 13:07:08 Functions: 100.0 % 16 16

            Line data    Source code
       1              : /**
       2              :  * @file HDF5Write_test.cxx Application that tests and demonstrates
       3              :  * the write functionality of the HDF5DataStore class.
       4              :  *
       5              :  * This is part of the DUNE DAQ Application Framework, copyright 2020.
       6              :  * Licensing/copyright details are in the COPYING file that you should have
       7              :  * received with this code.
       8              :  */
       9              : 
      10              : #include "dfmodules/DataStore.hpp"
      11              : 
      12              : #include "appmodel/DataWriterModule.hpp"
      13              : #include "appmodel/DataWriterConf.hpp"
      14              : #include "appmodel/FilenameParams.hpp"
      15              : #include "confmodel/DetectorConfig.hpp"
      16              : #include "confmodel/Session.hpp"
      17              : #include "appmodel/DataStoreConf.hpp"
      18              : #include "detdataformats/DetID.hpp"
      19              : 
      20              : #define BOOST_TEST_MODULE HDF5Write_test // NOLINT
      21              : 
      22              : #include "boost/test/unit_test.hpp"
      23              : 
      24              : #include <cstdlib>
      25              : #include <filesystem>
      26              : #include <fstream>
      27              : #include <iostream>
      28              : #include <memory>
      29              : #include <regex>
      30              : #include <string>
      31              : #include <utility>
      32              : #include <vector>
      33              : 
      34              : using namespace dunedaq::dfmodules;
      35              : 
      36              : std::vector<std::string>
      37           10 : get_files_matching_pattern(const std::string& path, const std::string& pattern)
      38              : {
      39           10 :   std::regex regex_search_pattern(pattern);
      40           10 :   std::vector<std::string> file_list;
      41       162710 :   for (const auto& entry : std::filesystem::directory_iterator(path)) {
      42       162700 :     if (std::regex_match(entry.path().filename().string(), regex_search_pattern)) {
      43           19 :       file_list.push_back(entry.path());
      44              :     }
      45           10 :   }
      46           10 :   return file_list;
      47           10 : }
      48              : 
      49              : std::vector<std::string>
      50           15 : delete_files_matching_pattern(const std::string& path, const std::string& pattern)
      51              : {
      52           15 :   std::regex regex_search_pattern(pattern);
      53           15 :   std::vector<std::string> file_list;
      54       244050 :   for (const auto& entry : std::filesystem::directory_iterator(path)) {
      55       244035 :     if (std::regex_match(entry.path().filename().string(), regex_search_pattern)) {
      56           15 :       if (std::filesystem::remove(entry.path())) {
      57           15 :         file_list.push_back(entry.path());
      58              :       }
      59              :     }
      60           15 :   }
      61           15 :   return file_list;
      62           15 : }
      63              : 
      64              : dunedaq::daqdataformats::TriggerRecord
      65           35 : create_trigger_record(int trig_num, int fragment_size, int element_count)
      66              : {
      67           35 :   const int run_number = 53;
      68           35 :   const dunedaq::daqdataformats::SourceID::Subsystem stype_to_use =
      69              :     dunedaq::daqdataformats::SourceID::Subsystem::kDetectorReadout;
      70              : 
      71              :   // setup our dummy_data
      72           35 :   std::vector<char> dummy_vector(fragment_size);
      73           35 :   char* dummy_data = dummy_vector.data();
      74              : 
      75              :   // get a timestamp for this trigger
      76           70 :   uint64_t ts = std::chrono::duration_cast<std::chrono::milliseconds>( // NOLINT(build/unsigned)
      77           35 :                   system_clock::now().time_since_epoch())
      78           35 :                   .count();
      79              : 
      80              :   // create TriggerRecordHeader
      81           35 :   dunedaq::daqdataformats::TriggerRecordHeaderData trh_data;
      82           35 :   trh_data.trigger_number = trig_num;
      83           35 :   trh_data.trigger_timestamp = ts;
      84           35 :   trh_data.num_requested_components = element_count;
      85           35 :   trh_data.run_number = run_number;
      86           35 :   trh_data.sequence_number = 0;
      87           35 :   trh_data.max_sequence_number = 1;
      88           35 :   trh_data.element_id = dunedaq::daqdataformats::SourceID(dunedaq::daqdataformats::SourceID::Subsystem::kTRBuilder, 0);
      89              : 
      90           35 :   dunedaq::daqdataformats::TriggerRecordHeader trh(&trh_data);
      91              : 
      92              :   // create out TriggerRecord
      93           35 :   dunedaq::daqdataformats::TriggerRecord tr(trh);
      94              : 
      95              :   // loop over elements
      96          855 :   for (int ele_num = 0; ele_num < element_count; ++ele_num) {
      97              : 
      98              :     // create our fragment
      99          820 :     dunedaq::daqdataformats::FragmentHeader fh;
     100          820 :     fh.trigger_number = trig_num;
     101          820 :     fh.trigger_timestamp = ts;
     102          820 :     fh.window_begin = ts - 10;
     103          820 :     fh.window_end = ts;
     104          820 :     fh.run_number = run_number;
     105          820 :     fh.sequence_number = 0;
     106          820 :     fh.fragment_type =
     107              :       static_cast<dunedaq::daqdataformats::fragment_type_t>(dunedaq::daqdataformats::FragmentType::kWIB);
     108          820 :     fh.detector_id = static_cast<uint16_t>(dunedaq::detdataformats::DetID::Subdetector::kHD_TPC);
     109          820 :     fh.element_id = dunedaq::daqdataformats::SourceID(stype_to_use, ele_num);
     110          820 :     std::unique_ptr<dunedaq::daqdataformats::Fragment> frag_ptr(
     111          820 :       new dunedaq::daqdataformats::Fragment(dummy_data, fragment_size));
     112          820 :     frag_ptr->set_header_fields(fh);
     113              : 
     114              :     // add fragment to TriggerRecord
     115          820 :     tr.add_fragment(std::move(frag_ptr));
     116              : 
     117          820 :   } // end loop over elements
     118              : 
     119           35 :   return tr;
     120           35 : }
     121              : 
     122              : struct CfgFixture
     123              : {
     124            5 :   CfgFixture(std::string sessionName)
     125            5 :   {
     126            5 :     TLOG_DEBUG(4) << "Creating CfgFixture";
     127            5 :     setenv("DUNEDAQ_SESSION", sessionName.c_str(), 1);
     128            5 :     std::string oksConfig = "oksconflibs:test/config/hdf5write_test.data.xml";
     129            5 :     std::string appName = "TestApp";
     130            5 :     cfgMgr = std::make_shared<dunedaq::appfwk::ConfigurationManager>(oksConfig, appName, sessionName);
     131            5 :     TLOG_DEBUG(4) << "Done with CfgFixture";
     132            5 :   }
     133              : 
     134              :   const dunedaq::confmodel::DetectorConfig* get_detector_config()
     135              :   {
     136              :     return cfgMgr->get_session()->get_detector_configuration();
     137              :   }
     138              : 
     139              :   std::shared_ptr<dunedaq::appfwk::ConfigurationManager> cfgMgr;
     140              : };
     141              : 
     142              : BOOST_AUTO_TEST_SUITE(HDF5Write_test)
     143              : 
     144            2 : BOOST_AUTO_TEST_CASE(WriteEventFiles)
     145              : {
     146            1 :   std::string file_path(std::filesystem::temp_directory_path());
     147              : 
     148            1 :   const int trigger_count = 5;
     149            1 :   const int apa_count = 3;
     150            1 :   const int link_count = 1;
     151            1 :   const int fragment_size = 10 + sizeof(dunedaq::daqdataformats::FragmentHeader);
     152              : 
     153              :   // delete any pre-existing files so that we start with a clean slate
     154            1 :   std::string delete_pattern = "hdf5writetest.*\\.hdf5";
     155            1 :   delete_files_matching_pattern(file_path, delete_pattern);
     156              : 
     157              :   // create the DataStore
     158            1 :   CfgFixture cfg("test-session-3-1");
     159            1 :   auto data_writer_conf = cfg.cfgMgr->get_dal<dunedaq::appmodel::DataWriterModule>("dwm-01")->get_configuration();
     160            1 :   auto data_store_conf = data_writer_conf->get_data_store_params();
     161              : 
     162            1 :   auto data_store_conf_obj = data_store_conf->config_object();
     163            1 :   data_store_conf_obj.set_by_val<std::string>("directory_path", file_path);
     164            1 :   data_store_conf_obj.set_by_val<std::string>("mode", "one-event-per-file");
     165              : 
     166            1 :   auto data_store_ptr = make_data_store(data_store_conf->get_type(), data_store_conf->UID(), cfg.cfgMgr, "dwm-01");
     167              : 
     168              :   // write several events, each with several fragments
     169            6 :   for (int trigger_number = 1; trigger_number <= trigger_count; ++trigger_number)
     170            5 :     data_store_ptr->write(create_trigger_record(trigger_number, fragment_size, link_count * apa_count));
     171              : 
     172            1 :   data_store_ptr.reset(); // explicit destruction
     173              : 
     174              :   // check that the expected number of files was created
     175            1 :   std::string search_pattern = "hdf5writetest.*\\.hdf5";
     176            1 :   std::vector<std::string> file_list = get_files_matching_pattern(file_path, search_pattern);
     177            1 :   BOOST_REQUIRE_EQUAL(file_list.size(), trigger_count);
     178              : 
     179              :   // clean up the files that were created
     180            1 :   file_list = delete_files_matching_pattern(file_path, delete_pattern);
     181            1 :   delete_files_matching_pattern(file_path, "HardwareMap.*\\.txt");
     182            1 :   BOOST_REQUIRE_EQUAL(file_list.size(), trigger_count);
     183            1 : }
     184              : 
     185            2 : BOOST_AUTO_TEST_CASE(WriteOneFile)
     186              : {
     187            1 :   std::string file_path(std::filesystem::temp_directory_path());
     188              : 
     189            1 :   const int trigger_count = 5;
     190            1 :   const int apa_count = 3;
     191            1 :   const int link_count = 1;
     192            1 :   const int fragment_size = 10 + sizeof(dunedaq::daqdataformats::FragmentHeader);
     193              : 
     194              :   // delete any pre-existing files so that we start with a clean slate
     195            1 :   std::string delete_pattern = "hdf5writetest.*\\.hdf5";
     196            1 :   delete_files_matching_pattern(file_path, delete_pattern);
     197              : 
     198              :   // create the DataStore
     199            1 :   CfgFixture cfg("test-session-3-1");
     200            1 :   auto data_writer_conf = cfg.cfgMgr->get_dal<dunedaq::appmodel::DataWriterModule>("dwm-01")->get_configuration();
     201            1 :   auto data_store_conf = data_writer_conf->get_data_store_params();
     202              : 
     203            1 :   auto data_store_conf_obj = data_store_conf->config_object();
     204            1 :   data_store_conf_obj.set_by_val<std::string>("directory_path", file_path);
     205              : 
     206            1 :   auto data_store_ptr = make_data_store(data_store_conf->get_type(), data_store_conf->UID(), cfg.cfgMgr, "dwm-01");
     207              :     
     208              :   // write several events, each with several fragments
     209            6 :   for (int trigger_number = 1; trigger_number <= trigger_count; ++trigger_number)
     210            5 :     data_store_ptr->write(create_trigger_record(trigger_number, fragment_size, apa_count * link_count));
     211              : 
     212            1 :   data_store_ptr.reset(); // explicit destruction
     213              : 
     214              :   // check that the expected number of files was created
     215            1 :   std::string search_pattern = "hdf5writetest.*\\.hdf5";
     216            1 :   std::vector<std::string> file_list = get_files_matching_pattern(file_path, search_pattern);
     217            1 :   BOOST_REQUIRE_EQUAL(file_list.size(), 1);
     218              : 
     219              :   // clean up the files that were created
     220            1 :   file_list = delete_files_matching_pattern(file_path, delete_pattern);
     221            1 :   delete_files_matching_pattern(file_path, "HardwareMap.*\\.txt");
     222            1 :   BOOST_REQUIRE_EQUAL(file_list.size(), 1);
     223            1 : }
     224              : 
     225            2 : BOOST_AUTO_TEST_CASE(CheckWritingSuffix)
     226              : {
     227            1 :   std::string file_path(std::filesystem::temp_directory_path());
     228              : 
     229            1 :   const int trigger_count = 5;
     230            1 :   const int apa_count = 3;
     231            1 :   const int link_count = 1;
     232            1 :   const int fragment_size = 10 + sizeof(dunedaq::daqdataformats::FragmentHeader);
     233              : 
     234              :   // delete any pre-existing files so that we start with a clean slate
     235            1 :   std::string delete_pattern = "hdf5writetest.*\\.hdf5";
     236            1 :   delete_files_matching_pattern(file_path, delete_pattern);
     237              : 
     238              :   // create the DataStore
     239            1 :   CfgFixture cfg("test-session-3-1");
     240            1 :   auto data_writer_conf = cfg.cfgMgr->get_dal<dunedaq::appmodel::DataWriterModule>("dwm-01")->get_configuration();
     241            1 :   auto data_store_conf = data_writer_conf->get_data_store_params();
     242              : 
     243            1 :   auto data_store_conf_obj = data_store_conf->config_object();
     244            1 :   data_store_conf_obj.set_by_val<std::string>("directory_path", file_path);
     245              : 
     246            1 :   auto data_store_ptr = make_data_store(data_store_conf->get_type(), data_store_conf->UID(), cfg.cfgMgr, "dwm-01");
     247              :   
     248              :   // write several events, each with several fragments
     249            6 :   for (int trigger_number = 1; trigger_number <= trigger_count; ++trigger_number) {
     250            5 :     data_store_ptr->write(create_trigger_record(trigger_number, fragment_size, apa_count * link_count));
     251              : 
     252              :     // check that the .writing file is there
     253            5 :     std::string search_pattern = "hdf5writetest.*\\.writing";
     254            5 :     std::vector<std::string> file_list = get_files_matching_pattern(file_path, search_pattern);
     255            5 :     BOOST_REQUIRE_EQUAL(file_list.size(), 1);
     256            5 :   }
     257              : 
     258            1 :   data_store_ptr.reset(); // explicit destruction
     259              : 
     260              :   // check that the expected number of files was created
     261            1 :   std::string search_pattern = "hdf5writetest.*\\.writing";
     262            1 :   std::vector<std::string> file_list = get_files_matching_pattern(file_path, search_pattern);
     263            1 :   BOOST_REQUIRE_EQUAL(file_list.size(), 0);
     264              : 
     265              :   // clean up the files that were created
     266            1 :   file_list = delete_files_matching_pattern(file_path, delete_pattern);
     267            1 :   delete_files_matching_pattern(file_path, "HardwareMap.*\\.txt");
     268            1 :   BOOST_REQUIRE_EQUAL(file_list.size(), 1);
     269            1 : }
     270              : 
     271            2 : BOOST_AUTO_TEST_CASE(FileSizeLimitResultsInMultipleFiles)
     272              : {
     273            1 :   std::string file_path(std::filesystem::temp_directory_path());
     274              : 
     275            1 :   const int trigger_count = 15;
     276            1 :   const int apa_count = 5;
     277            1 :   const int link_count = 10;
     278            1 :   const int fragment_size = 10000;
     279              : 
     280              :   // 5 APAs times 10 links times 10000 bytes per fragment gives 500,000 bytes per TR
     281              :   // So, 15 TRs would give 7,500,000 bytes total.
     282              : 
     283              :   // delete any pre-existing files so that we start with a clean slate
     284            1 :   std::string delete_pattern = "hdf5writetest.*\\.hdf5";
     285            1 :   delete_files_matching_pattern(file_path, delete_pattern);
     286              : 
     287              :   // create the DataStore
     288            1 :   CfgFixture cfg("test-session-5-10");
     289            1 :   auto data_writer_conf = cfg.cfgMgr->get_dal<dunedaq::appmodel::DataWriterModule>("dwm-01")->get_configuration();
     290            1 :   auto data_store_conf = data_writer_conf->get_data_store_params();
     291              : 
     292            1 :   auto data_store_conf_obj = data_store_conf->config_object();
     293            1 :   data_store_conf_obj.set_by_val<std::string>("directory_path", file_path);
     294            1 :   data_store_conf_obj.set_by_val<int>("max_file_size", 3000000); // goal is 6 events per file
     295              : 
     296            1 :   auto data_store_ptr = make_data_store(data_store_conf->get_type(), data_store_conf->UID(), cfg.cfgMgr, "dwm-01");
     297              :   
     298              :   // write several events, each with several fragments
     299           16 :   for (int trigger_number = 1; trigger_number <= trigger_count; ++trigger_number)
     300           15 :     data_store_ptr->write(create_trigger_record(trigger_number, fragment_size, apa_count * link_count));
     301              : 
     302            1 :   data_store_ptr.reset(); // explicit destruction
     303              : 
     304              :   // check that the expected number of files was created
     305            1 :   std::string search_pattern = "hdf5writetest.*\\.hdf5";
     306            1 :   std::vector<std::string> file_list = get_files_matching_pattern(file_path, search_pattern);
     307              :   // 7,500,000 bytes stored in files of size 3,000,000 should result in three files.
     308            1 :   BOOST_REQUIRE_EQUAL(file_list.size(), 3);
     309              : 
     310              :   // clean up the files that were created
     311            1 :   file_list = delete_files_matching_pattern(file_path, delete_pattern);
     312            1 :   delete_files_matching_pattern(file_path, "HardwareMap.*\\.txt");
     313            1 :   BOOST_REQUIRE_EQUAL(file_list.size(), 3);
     314            1 : }
     315              : 
     316            2 : BOOST_AUTO_TEST_CASE(SmallFileSizeLimitDataBlockListWrite)
     317              : {
     318            1 :   std::string file_path(std::filesystem::temp_directory_path());
     319              : 
     320            1 :   const int trigger_count = 5;
     321            1 :   const int apa_count = 5;
     322            1 :   const int link_count = 1;
     323            1 :   const int fragment_size = 100000;
     324              : 
     325              :   // 5 APAs times 100000 bytes per fragment gives 500,000 bytes per TR
     326              : 
     327              :   // delete any pre-existing files so that we start with a clean slate
     328            1 :   std::string delete_pattern = "hdf5writetest.*\\.hdf5";
     329            1 :   delete_files_matching_pattern(file_path, delete_pattern);
     330              : 
     331              :   // create the DataStore
     332            1 :   CfgFixture cfg("test-session-5-1");
     333            1 :   auto data_writer_conf = cfg.cfgMgr->get_dal<dunedaq::appmodel::DataWriterModule>("dwm-01")->get_configuration();
     334            1 :   auto data_store_conf = data_writer_conf->get_data_store_params();
     335              : 
     336            1 :   auto data_store_conf_obj = data_store_conf->config_object();
     337            1 :   data_store_conf_obj.set_by_val<std::string>("directory_path", file_path);
     338            1 :   data_store_conf_obj.set_by_val<int>("max_file_size", 150000); // ~1.5 Fragment, ~0.3 TR
     339              : 
     340            1 :   auto data_store_ptr = make_data_store(data_store_conf->get_type(), data_store_conf->UID(), cfg.cfgMgr,"dwm-01");
     341              : 
     342              :   // write several events, each with several fragments
     343            6 :   for (int trigger_number = 1; trigger_number <= trigger_count; ++trigger_number)
     344            5 :     data_store_ptr->write(create_trigger_record(trigger_number, fragment_size, apa_count * link_count));
     345              : 
     346            1 :   data_store_ptr.reset(); // explicit destruction
     347              : 
     348              :   // check that the expected number of files was created
     349            1 :   std::string search_pattern = "hdf5writetest.*\\.hdf5";
     350            1 :   std::vector<std::string> file_list = get_files_matching_pattern(file_path, search_pattern);
     351              :   // each TriggerRecord should be stored in its own file
     352            1 :   BOOST_REQUIRE_EQUAL(file_list.size(), 5);
     353              : 
     354              :   // clean up the files that were created
     355            1 :   file_list = delete_files_matching_pattern(file_path, delete_pattern);
     356            1 :   delete_files_matching_pattern(file_path, "HardwareMap.*\\.txt");
     357            1 :   BOOST_REQUIRE_EQUAL(file_list.size(), 5);
     358            1 : }
     359              : 
     360              : BOOST_AUTO_TEST_SUITE_END()
        

Generated by: LCOV version 2.0-1