LCOV - code coverage report
Current view: top level - hdf5libs/unittest - HDF5WriteReadTimeSlice_test.cxx (source / functions) Coverage Total Hit
Test: code.result Lines: 100.0 % 211 211
Test Date: 2026-10-11 15:05:20 Functions: 100.0 % 19 19

            Line data    Source code
       1              : /**
       2              :  * @file HDF5WriteReadTimeSlice_test.cxx Application that tests and demonstrates
       3              :  * the write/read functions of the HDF5RawDataFile 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 "hdf5libs/HDF5RawDataFile.hpp"
      11              : #include "hdf5libs/test/HDF5TestUtils.hpp"
      12              : 
      13              : #include "detdataformats/DetID.hpp"
      14              : 
      15              : #define BOOST_TEST_MODULE HDF5WriteReadTimeSlice_test // NOLINT
      16              : 
      17              : #include "boost/test/unit_test.hpp"
      18              : 
      19              : #include <cstdlib>
      20              : #include <filesystem>
      21              : #include <fstream>
      22              : #include <iostream>
      23              : #include <memory>
      24              : #include <regex>
      25              : #include <string>
      26              : #include <utility>
      27              : #include <vector>
      28              : 
      29              : using namespace dunedaq::hdf5libs;
      30              : 
      31              : constexpr int run_number = 53;
      32              : constexpr int file_index = 0;
      33              : const std::string application_name = "HDF5WriteReadTimeSlice_test";
      34              : constexpr size_t fragment_size = 100;
      35              : constexpr size_t element_count_tpc = 4;
      36              : constexpr size_t element_count_pds = 4;
      37              : size_t compressed_raw_data_size = 0;
      38              : size_t uncompressed_raw_data_size = 0;
      39              : 
      40              : const size_t components_per_record = element_count_tpc + element_count_pds;
      41              : 
      42              : HDF5FileLayoutParameters
      43           12 : create_file_layout_params()
      44              : {
      45           12 :   dunedaq::hdf5libs::HDF5PathParameters params_tpc;
      46           12 :   params_tpc.detector_group_type = "Detector_Readout";
      47           12 :   params_tpc.detector_group_name = "TPC";
      48           12 :   params_tpc.element_name_prefix = "Link";
      49           12 :   params_tpc.digits_for_element_number = 5;
      50              : 
      51              :   // dunedaq::hdf5libs::hdf5filelayout::PathParams params_pds;
      52              :   // params_pds.detector_group_type = "PDS";
      53              :   // params_pds.detector_group_name = "PDS";
      54              :   // params_pds.element_name_prefix = "Element";
      55              :   // params_pds.digits_for_element_number = 5;
      56              : 
      57              :   // note, for unit test json equality checks, 'PDS' needs to come before
      58              :   //'TPC', as on reading back the filelayout it looks like it's alphabetical.
      59           12 :   std::vector<dunedaq::hdf5libs::HDF5PathParameters> param_list;
      60              :   // param_list.push_back(params_pds);
      61           12 :   param_list.push_back(params_tpc);
      62              : 
      63           12 :   dunedaq::hdf5libs::HDF5FileLayoutParameters layout_params;
      64           12 :   layout_params.path_params_list = param_list;
      65           12 :   layout_params.record_name_prefix = "TimeSlice";
      66           12 :   layout_params.digits_for_record_number = 6;
      67           12 :   layout_params.digits_for_sequence_number = 0;
      68           12 :   layout_params.record_header_dataset_name = "TimeSliceHeader";
      69              : 
      70           12 :   return layout_params;
      71           12 : }
      72              : 
      73              : dunedaq::daqdataformats::TimeSlice
      74           30 : create_timeslice(int ts_num)
      75              : {
      76              :   // setup our dummy_data
      77           30 :   std::vector<char> dummy_vector(fragment_size);
      78           30 :   char* dummy_data = dummy_vector.data();
      79              : 
      80              :   // get a timestamp for this trigger
      81           30 :   int64_t timestamp =
      82           30 :     std::chrono::duration_cast<std::chrono::milliseconds>(system_clock::now().time_since_epoch()).count();
      83              : 
      84              :   // create TimeSliceHeader
      85           30 :   dunedaq::daqdataformats::TimeSliceHeader tsh;
      86           30 :   tsh.timeslice_number = ts_num;
      87           30 :   tsh.run_number = run_number;
      88           30 :   tsh.element_id = dunedaq::daqdataformats::SourceID(dunedaq::daqdataformats::SourceID::Subsystem::kTRBuilder, 0);
      89              : 
      90              :   // create our TimeSlice
      91           30 :   dunedaq::daqdataformats::TimeSlice ts(tsh);
      92              : 
      93              :   // loop over elements tpc
      94          150 :   for (size_t ele_num = 0; ele_num < element_count_tpc; ++ele_num) {
      95              : 
      96              :     // create our fragment
      97          120 :     dunedaq::daqdataformats::FragmentHeader fh;
      98          120 :     fh.trigger_number = ts_num;
      99          120 :     fh.trigger_timestamp = timestamp;
     100          120 :     fh.window_begin = timestamp;
     101          120 :     fh.window_end = timestamp;
     102          120 :     fh.run_number = run_number;
     103          120 :     fh.fragment_type = 0;
     104          120 :     fh.fragment_type =
     105              :       static_cast<dunedaq::daqdataformats::fragment_type_t>(dunedaq::daqdataformats::FragmentType::kWIB);
     106          120 :     fh.sequence_number = 0;
     107          120 :     fh.detector_id = static_cast<uint16_t>(dunedaq::detdataformats::DetID::Subdetector::kHD_TPC);
     108          240 :     fh.element_id =
     109          120 :       dunedaq::daqdataformats::SourceID(dunedaq::daqdataformats::SourceID::Subsystem::kDetectorReadout, ele_num);
     110              : 
     111          120 :     std::unique_ptr<dunedaq::daqdataformats::Fragment> frag_ptr(
     112          120 :       new dunedaq::daqdataformats::Fragment(dummy_data, fragment_size));
     113          120 :     frag_ptr->set_header_fields(fh);
     114              : 
     115              :     // add fragment to TriggerRecord
     116          120 :     ts.add_fragment(std::move(frag_ptr));
     117              : 
     118          120 :   } // end loop over elements
     119              : 
     120              :   // loop over elements pds
     121          150 :   for (size_t ele_num = 0; ele_num < element_count_pds; ++ele_num) {
     122              : 
     123              :     // create our fragment
     124          120 :     dunedaq::daqdataformats::FragmentHeader fh;
     125          120 :     fh.trigger_number = ts_num;
     126          120 :     fh.trigger_timestamp = timestamp;
     127          120 :     fh.window_begin = timestamp;
     128          120 :     fh.window_end = timestamp;
     129          120 :     fh.run_number = run_number;
     130          120 :     fh.fragment_type =
     131              :       static_cast<dunedaq::daqdataformats::fragment_type_t>(dunedaq::daqdataformats::FragmentType::kDAPHNE);
     132          120 :     fh.sequence_number = 0;
     133          120 :     fh.detector_id = static_cast<uint16_t>(dunedaq::detdataformats::DetID::Subdetector::kHD_PDS);
     134          240 :     fh.element_id = dunedaq::daqdataformats::SourceID(dunedaq::daqdataformats::SourceID::Subsystem::kDetectorReadout,
     135          120 :                                                       ele_num + element_count_tpc);
     136              : 
     137          120 :     std::unique_ptr<dunedaq::daqdataformats::Fragment> frag_ptr(
     138          120 :       new dunedaq::daqdataformats::Fragment(dummy_data, fragment_size));
     139          120 :     frag_ptr->set_header_fields(fh);
     140              : 
     141              :     // add fragment to TriggerRecord
     142          120 :     ts.add_fragment(std::move(frag_ptr));
     143              : 
     144          120 :   } // end loop over elements
     145              : 
     146           30 :   return ts;
     147           30 : }
     148              : 
     149              : struct FileWriteFixture
     150              : {
     151            6 :   FileWriteFixture(int num_slices = 5, unsigned comp_lvl = 0)
     152            6 :     : timeslice_count(num_slices)
     153            6 :     , compression_level(comp_lvl)
     154            6 :     , file_path(std::filesystem::temp_directory_path())
     155           12 :     , hdf5_filename("demo" + std::to_string(getpid()) + "_" + std::string(getenv("USER")) + "_comp" +
     156           18 :                     std::to_string(compression_level) + ".hdf5")
     157            6 :     , fl_pars(create_file_layout_params())
     158            6 :     , recorded_size_at_write(0)
     159              :   {
     160            6 :     delete_files_matching_pattern(file_path, hdf5_filename);
     161              : 
     162              :     // convert file_params to json, allows for easy comp later
     163            6 :     auto fl_pars = create_file_layout_params();
     164              : 
     165              :     // create src-geo id map
     166            6 :     auto srcid_geoid_map = create_srcid_geoid_map();
     167              :     // create the file
     168           12 :     std::unique_ptr<HDF5RawDataFile> h5file_ptr(new HDF5RawDataFile(file_path + "/" + hdf5_filename,
     169              :                                                                     run_number,
     170              :                                                                     file_index,
     171              :                                                                     application_name,
     172              :                                                                     fl_pars,
     173              :                                                                     srcid_geoid_map,
     174           18 :                                                                     compression_level));
     175              : 
     176              :     // write several events, each with several fragments
     177           36 :     for (int timeslice_number = 1; timeslice_number <= timeslice_count; ++timeslice_number)
     178           30 :       h5file_ptr->write(create_timeslice(timeslice_number));
     179              : 
     180              :     // get recorded size for checking
     181            6 :     recorded_size_at_write = h5file_ptr->get_recorded_size();
     182              : 
     183            6 :     h5file_ptr.reset(); // explicit destruction
     184            6 :   }
     185              : 
     186            6 :   ~FileWriteFixture() { delete_files_matching_pattern(file_path, hdf5_filename); }
     187              : 
     188            2 :   void read_file_attributes()
     189              :   {
     190              :     // open file for reading now
     191              :     // std::unique_ptr<HDF5RawDataFile> h5file_ptr = std::make_unique<HDF5RawDataFile>(file_path + "/" + hdf5_filename);
     192            2 :     h5file_ptr.reset(new HDF5RawDataFile(file_path + "/" + hdf5_filename));
     193              : 
     194              :     // check attributes
     195            2 :     auto recorded_size_attr = h5file_ptr->get_attribute<size_t>("recorded_size");
     196            2 :     auto run_number_attr = h5file_ptr->get_attribute<size_t>("run_number");
     197            2 :     auto file_index_attr = h5file_ptr->get_attribute<size_t>("file_index");
     198            2 :     auto app_name_attr = h5file_ptr->get_attribute<std::string>("application_name");
     199            2 :     auto record_type_attr = h5file_ptr->get_attribute<std::string>("record_type");
     200            2 :     auto compression_level_attr = h5file_ptr->get_attribute<unsigned>("compression_level");
     201              : 
     202            2 :     BOOST_REQUIRE_EQUAL(recorded_size_at_write, recorded_size_attr);
     203            2 :     BOOST_REQUIRE_EQUAL(run_number, run_number_attr);
     204            2 :     BOOST_REQUIRE_EQUAL(file_index, file_index_attr);
     205            2 :     BOOST_REQUIRE_EQUAL(application_name, app_name_attr);
     206            2 :     BOOST_REQUIRE_EQUAL("TimeSlice", record_type_attr);
     207            2 :     BOOST_REQUIRE_EQUAL(this->compression_level, compression_level_attr);
     208              : 
     209              :     // extract and check file layout parameters
     210            2 :     auto file_layout_parameters_read = h5file_ptr->get_file_layout().get_file_layout_params();
     211            2 :     BOOST_REQUIRE_EQUAL(fl_pars.to_json(), file_layout_parameters_read.to_json());
     212              : 
     213            2 :     if (this->compression_level == 0) {
     214            1 :       uncompressed_raw_data_size = recorded_size_at_write;
     215              :     } else {
     216            1 :       compressed_raw_data_size = recorded_size_at_write;
     217            1 :       BOOST_ASSERT(compressed_raw_data_size < uncompressed_raw_data_size);
     218              :     }
     219            2 :   }
     220              : 
     221            2 :   void read_file_datasets()
     222              :   {
     223              :     // open file for reading now
     224            2 :     h5file_ptr.reset(new HDF5RawDataFile(file_path + "/" + hdf5_filename));
     225              : 
     226            2 :     auto timeslices = h5file_ptr->get_all_timeslice_numbers();
     227            2 :     BOOST_REQUIRE_EQUAL(timeslice_count, timeslices.size());
     228              : 
     229            2 :     auto first_timeslice = *(timeslices.begin());
     230            2 :     auto last_timeslice = *(std::next(timeslices.begin(), timeslices.size() - 1));
     231            2 :     BOOST_REQUIRE_EQUAL(1, first_timeslice);
     232            2 :     BOOST_REQUIRE_EQUAL(timeslice_count, last_timeslice);
     233              : 
     234            2 :     auto all_datasets = h5file_ptr->get_dataset_paths();
     235            2 :     BOOST_REQUIRE_EQUAL(timeslice_count * (1 + components_per_record), all_datasets.size());
     236              : 
     237            2 :     auto all_tsh_paths = h5file_ptr->get_timeslice_header_dataset_paths();
     238            2 :     BOOST_REQUIRE_EQUAL(timeslice_count, all_tsh_paths.size());
     239              : 
     240            2 :     auto all_frag_paths = h5file_ptr->get_all_fragment_dataset_paths();
     241            2 :     BOOST_REQUIRE_EQUAL(timeslice_count * components_per_record, all_frag_paths.size());
     242              : 
     243              :     // test access by name
     244            2 :     std::unique_ptr<dunedaq::daqdataformats::TimeSliceHeader> trs_ptr;
     245            2 :     trs_ptr = h5file_ptr->get_tsh_ptr(all_tsh_paths.at(2));
     246            2 :     BOOST_REQUIRE_EQUAL(trs_ptr->timeslice_number, 3);
     247            2 :     BOOST_REQUIRE_EQUAL(trs_ptr->run_number, run_number);
     248              : 
     249              :     // test access by trigger number
     250            2 :     trs_ptr = h5file_ptr->get_tsh_ptr(2);
     251            2 :     BOOST_REQUIRE_EQUAL(trs_ptr->timeslice_number, 2);
     252            2 :     BOOST_REQUIRE_EQUAL(trs_ptr->run_number, run_number);
     253              : 
     254            2 :     std::unique_ptr<dunedaq::daqdataformats::Fragment> frag_ptr;
     255              : 
     256              :     // test access by name
     257            2 :     frag_ptr = h5file_ptr->get_frag_ptr(all_frag_paths.back());
     258            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_trigger_number(), last_timeslice);
     259            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_run_number(), run_number);
     260              : 
     261              :     // test access by trigger number, type, element
     262            2 :     frag_ptr = h5file_ptr->get_frag_ptr(2, 0, "Detector_Readout", 0);
     263            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_trigger_number(), 2);
     264            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_run_number(), run_number);
     265            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_element_id().subsystem,
     266              :                         dunedaq::daqdataformats::SourceID::Subsystem::kDetectorReadout);
     267            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_element_id().id, 0);
     268              : 
     269              :     // test access by trigger number, type, element
     270            2 :     frag_ptr = h5file_ptr->get_frag_ptr(4, 0, "Detector_Readout", 4);
     271            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_trigger_number(), 4);
     272            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_run_number(), run_number);
     273            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_element_id().subsystem,
     274              :                         dunedaq::daqdataformats::SourceID::Subsystem::kDetectorReadout);
     275            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_element_id().id, 4);
     276              : 
     277              :     // test access by passing in SourceID
     278            2 :     dunedaq::daqdataformats::SourceID gid = { dunedaq::daqdataformats::SourceID::Subsystem::kDetectorReadout, 1 };
     279            2 :     frag_ptr = h5file_ptr->get_frag_ptr(5, 0, gid);
     280            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_trigger_number(), 5);
     281            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_run_number(), run_number);
     282            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_element_id().subsystem,
     283              :                         dunedaq::daqdataformats::SourceID::Subsystem::kDetectorReadout);
     284            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_element_id().id, 1);
     285            2 :   }
     286              : 
     287            2 :   void read_file_max_sequence()
     288              :   {
     289              :     // open file for reading now
     290            2 :     h5file_ptr.reset(new HDF5RawDataFile(file_path + "/" + hdf5_filename));
     291              : 
     292            2 :     auto timeslices = h5file_ptr->get_all_timeslice_numbers();
     293            2 :     BOOST_REQUIRE_EQUAL(timeslice_count, timeslices.size());
     294              : 
     295            2 :     auto first_timeslice = *(timeslices.begin());
     296            2 :     auto last_timeslice = *(std::next(timeslices.begin(), timeslices.size() - 1));
     297            2 :     BOOST_REQUIRE_EQUAL(1, first_timeslice);
     298            2 :     BOOST_REQUIRE_EQUAL(timeslice_count, last_timeslice);
     299              : 
     300            2 :     auto all_datasets = h5file_ptr->get_dataset_paths();
     301            2 :     BOOST_REQUIRE_EQUAL(timeslice_count * (1 + components_per_record), all_datasets.size());
     302              : 
     303            2 :     auto all_tsh_paths = h5file_ptr->get_timeslice_header_dataset_paths();
     304            2 :     BOOST_REQUIRE_EQUAL(timeslice_count, all_tsh_paths.size());
     305              : 
     306            2 :     auto all_frag_paths = h5file_ptr->get_all_fragment_dataset_paths();
     307            2 :     BOOST_REQUIRE_EQUAL(timeslice_count * components_per_record, all_frag_paths.size());
     308              : 
     309              :     // test access by name
     310            2 :     std::unique_ptr<dunedaq::daqdataformats::TimeSliceHeader> trs_ptr;
     311            2 :     trs_ptr = h5file_ptr->get_tsh_ptr(all_tsh_paths.at(2));
     312            2 :     BOOST_REQUIRE_EQUAL(trs_ptr->timeslice_number, 3);
     313            2 :     BOOST_REQUIRE_EQUAL(trs_ptr->run_number, run_number);
     314              : 
     315              :     // test access by trigger number
     316            2 :     trs_ptr = h5file_ptr->get_tsh_ptr(2);
     317            2 :     BOOST_REQUIRE_EQUAL(trs_ptr->timeslice_number, 2);
     318            2 :     BOOST_REQUIRE_EQUAL(trs_ptr->run_number, run_number);
     319              : 
     320            2 :     std::unique_ptr<dunedaq::daqdataformats::Fragment> frag_ptr;
     321              : 
     322              :     // test access by name
     323            2 :     frag_ptr = h5file_ptr->get_frag_ptr(all_frag_paths.back());
     324            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_trigger_number(), last_timeslice);
     325            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_run_number(), run_number);
     326              : 
     327              :     // test access by trigger number, type, element
     328            2 :     frag_ptr = h5file_ptr->get_frag_ptr(2, 0, "Detector_Readout", 0);
     329            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_trigger_number(), 2);
     330            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_run_number(), run_number);
     331            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_element_id().subsystem,
     332              :                         dunedaq::daqdataformats::SourceID::Subsystem::kDetectorReadout);
     333            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_element_id().id, 0);
     334              : 
     335              :     // test access by trigger number, type, element
     336            2 :     frag_ptr = h5file_ptr->get_frag_ptr(4, 0, "Detector_Readout", 4);
     337            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_trigger_number(), 4);
     338            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_run_number(), run_number);
     339            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_element_id().subsystem,
     340              :                         dunedaq::daqdataformats::SourceID::Subsystem::kDetectorReadout);
     341            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_element_id().id, 4);
     342              : 
     343              :     // test access by passing in SourceID
     344            2 :     dunedaq::daqdataformats::SourceID gid = { dunedaq::daqdataformats::SourceID::Subsystem::kDetectorReadout, 1 };
     345            2 :     frag_ptr = h5file_ptr->get_frag_ptr(5, 0, gid);
     346            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_trigger_number(), 5);
     347            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_run_number(), run_number);
     348            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_element_id().subsystem,
     349              :                         dunedaq::daqdataformats::SourceID::Subsystem::kDetectorReadout);
     350            2 :     BOOST_REQUIRE_EQUAL(frag_ptr->get_element_id().id, 1);
     351            2 :   }
     352              : 
     353              :   int timeslice_count;
     354              :   unsigned compression_level;
     355              :   std::string file_path;
     356              :   std::string hdf5_filename;
     357              :   HDF5FileLayoutParameters fl_pars;
     358              :   size_t recorded_size_at_write;
     359              :   std::unique_ptr<HDF5RawDataFile> h5file_ptr;
     360              : };
     361              : 
     362              : BOOST_AUTO_TEST_SUITE(HDF5WriteReadTimeSlice_test)
     363              : 
     364            2 : BOOST_AUTO_TEST_CASE(ReadFileAttributes)
     365              : {
     366            1 :   FileWriteFixture fixture(5, 0);
     367            1 :   fixture.read_file_attributes();
     368            1 : }
     369              : 
     370            2 : BOOST_AUTO_TEST_CASE(ReadCompressedFileAttributes)
     371              : {
     372            1 :   FileWriteFixture fixture(5, 1);
     373            1 :   fixture.read_file_attributes();
     374            1 : }
     375              : 
     376            2 : BOOST_AUTO_TEST_CASE(ReadFileDatasets)
     377              : {
     378            1 :   FileWriteFixture fixture(5, 0);
     379            1 :   fixture.read_file_datasets();
     380            1 : }
     381              : 
     382            2 : BOOST_AUTO_TEST_CASE(ReadCompressedFileDatasets)
     383              : {
     384            1 :   FileWriteFixture fixture(5, 1);
     385            1 :   fixture.read_file_datasets();
     386            1 : }
     387              : 
     388            2 : BOOST_AUTO_TEST_CASE(ReadFileMaxSequence)
     389              : {
     390            1 :   FileWriteFixture fixture(5, 0);
     391            1 :   fixture.read_file_max_sequence();
     392            1 : }
     393              : 
     394            2 : BOOST_AUTO_TEST_CASE(ReadCompressedFileMaxSequence)
     395              : {
     396            1 :   FileWriteFixture fixture(5, 1);
     397            1 :   fixture.read_file_max_sequence();
     398            1 : }
     399              : 
     400              : BOOST_AUTO_TEST_SUITE_END()
        

Generated by: LCOV version 2.0-1