LDMX Software
FunctionalCoreTest.cxx
1#include <catch2/catch_approx.hpp>
2#include <catch2/catch_test_macros.hpp>
3#include <catch2/matchers/catch_matchers.hpp>
4
5#include "Framework/EventFile.h"
7#include "Framework/Process.h"
8#include "Framework/RunHeader.h"
12#include "TFile.h" //to open and check root files
13#include "TTreeReader.h" //to check output event files
14
15using Catch::Approx;
16
17namespace framework {
18namespace test {
19
40class TestProducer : public Producer {
43
46
49
50 public:
51 TestProducer(const std::string& name, Process& p) : Producer(name, p) {}
52 ~TestProducer() {}
53
54 void configure(framework::config::Parameters& p) final override {
55 create_run_header_ = p.get<bool>("create_run_header");
56 throw_after_ = p.get<int>("throw_after", -1);
57 }
58
59 void beforeNewRun(ldmx::RunHeader& header) final override {
60 if (not create_run_header_) return;
61 header.setIntParameter("Should Be Run Number", header.getRunNumber());
62 return;
63 }
64
65 void produce(framework::Event& event) final override {
66 int i_event = event.getEventNumber();
67
68 REQUIRE(i_event > 0);
69
70 // stand-in for a kill mid-write
71 if (throw_after_ > 0 and i_event > throw_after_) {
72 EXCEPTION_RAISE("TestKill", "Pretending to be killed mid-run.");
73 }
74
75 std::vector<ldmx::CalorimeterHit> calo_hits;
76 for (int i = 0; i < i_event; i++) {
77 calo_hits.emplace_back();
78 calo_hits.back().setID(i_event * 10 + i);
79 }
80
81 REQUIRE_NOTHROW(event.add("TestCollection", calo_hits));
82
83 ldmx::HcalHit max_pe_hit;
84 max_pe_hit.setID(i_event);
85
87 res.setMaxPEHit(max_pe_hit);
88 res.setVetoResult(i_event % 2 == 0);
89
90 REQUIRE_NOTHROW(event.add("TestObject", res));
91
92 events_ = i_event;
93
94 std::vector<int> event_indices = {i_event, i_event};
95 REQUIRE_NOTHROW(event.add("EventIndex", event_indices));
96
97 float test_float = i_event * 0.1;
98 REQUIRE_NOTHROW(event.add("EventTenth", test_float));
99
100 if (res.passesVeto()) setStorageHint(StorageControl::Hint::MustKeep);
101
102 return;
103 }
104}; // TestProducer
105
115class TestAnalyzer : public Analyzer {
116 private:
117 std::string test_collection_passname_;
118 std::string test_object_passname_;
119 std::string veto_test_object_passname_;
120 std::string tenth_event_passname_;
121 std::string event_index_passname_;
122
123 public:
124 TestAnalyzer(const std::string& name, Process& p) : Analyzer(name, p) {}
125 ~TestAnalyzer() {}
126
128 test_collection_passname_ =
129 ps.get<std::string>("test_collection_passname", "");
130 test_object_passname_ = ps.get<std::string>("test_object_passname", "");
131 veto_test_object_passname_ =
132 ps.get<std::string>("veto_test_object_passname", "");
133 tenth_event_passname_ = ps.get<std::string>("tenth_event_passname", "");
134 event_index_passname_ = ps.get<std::string>("event_index_passname", "");
135 }
136
137 void onProcessStart() final override {
138 REQUIRE_NOTHROW(getHistoDirectory());
139 test_hist_ = new TH1F("test_hist_", "Test Histogram", 101, -50, 50);
140 test_hist_->SetCanExtend(TH1::kAllAxes);
141 }
142
143 void analyze(const framework::Event& event) final override {
144 int i_event = event.getEventNumber();
145
146 REQUIRE(i_event > 0);
147
148 const std::vector<ldmx::CalorimeterHit>& calo_hits =
149 event.getCollection<ldmx::CalorimeterHit>("TestCollection",
150 test_collection_passname_);
151
152 CHECK(calo_hits.size() == i_event);
153 for (unsigned int i = 0; i < calo_hits.size(); i++) {
154 CHECK(calo_hits.at(i).getID() == i_event * 10 + i);
155 test_hist_->Fill(calo_hits.at(i).getID());
156 }
157
158 const ldmx::HcalVetoResult& veto_res =
159 event.getObject<ldmx::HcalVetoResult>("TestObject",
160 veto_test_object_passname_);
161
162 auto max_pe_hit{veto_res.getMaxPEHit()};
163
164 CHECK(max_pe_hit.getID() == i_event);
165 CHECK(veto_res.passesVeto() == (i_event % 2 == 0));
166
167 const float& tenth_event =
168 event.getObject<float>("EventTenth", tenth_event_passname_);
169 CHECK(tenth_event == Approx(i_event * 0.1));
170
171 const std::vector<int>& i_event_from_bus =
172 event.getCollection<int>("EventIndex", event_index_passname_);
173
174 CHECK(i_event_from_bus.size() == 2);
175 CHECK(i_event_from_bus.at(0) == i_event);
176 CHECK(i_event_from_bus.at(1) == i_event);
177
178 return;
179 }
180
181 private:
184}; // TestAnalyzer
185
201class IsGoodhistogramFile : public Catch::Matchers::MatcherBase<std::string> {
202 private:
205
206 public:
213
221 bool match(const std::string& filename) const override {
222 // Open file
223 TFile* f = TFile::Open(filename.c_str());
224 if (!f) return false;
225 TDirectory* d = (TDirectory*)f->Get("TestAnalyzer");
226 if (!d) return false;
227 TH1F* h = (TH1F*)d->Get("test_hist_");
228 if (!h) return false;
229
230 return (h->GetEntries() == correct_get_entries_);
231 }
232
239 virtual std::string describe() const override {
240 std::ostringstream ss;
241 ss << "has the histogram 'TestAnalyzer/test_hist_' with the number of "
242 "entries "
244 return ss.str();
245 }
246}; // isGoodhistogram_file
247
263class IsGoodEventFile : public Catch::Matchers::MatcherBase<std::string> {
264 private:
266 std::string pass_;
267
270
272 int runs_;
273
276
279
280 public:
286 IsGoodEventFile(const std::string& pass, const int& entries, const int& runs,
287 bool existColl = true, bool existObj = true)
288 : pass_(pass),
289 entries_(entries),
290 runs_(runs),
291 exist_collection_(existColl),
292 exist_object_(existObj) {}
293
302 bool match(const std::string& filename) const override {
303 TFile* f = TFile::Open(filename.c_str());
304 if (!f) return false;
305
306 TTreeReader events("LDMX_Events", f);
307
308 if (events.GetEntries(true) != entries_) {
309 f->Close();
310 return false;
311 }
312
313 // Event tree should _always_ have the ldmx::EventHeader
314 TTreeReaderValue<ldmx::EventHeader> header(events, "EventHeader");
315
316 if (exist_collection_) {
317 // make sure collection matches pattern
318 TTreeReaderValue<std::vector<ldmx::CalorimeterHit>> collection(
319 events, ("TestCollection_" + pass_).c_str());
320 while (events.Next()) {
321 if (collection->size() != header->getEventNumber()) {
322 f->Close();
323 return false;
324 }
325 for (unsigned int i = 0; i < collection->size(); i++)
326 if (collection->at(i).getID() != header->getEventNumber() * 10 + i) {
327 f->Close();
328 return false;
329 }
330 }
331 // restart in case checking object as well
332 events.Restart();
333 } else {
334 // check to make sure collection is NOT there
335 auto t{(TTree*)f->Get("LDMX_Events")};
336 if (t and t->GetBranch(("TestCollection_" + pass_).c_str())) {
337 f->Close();
338 return false;
339 }
340 }
341
342 if (exist_object_) {
343 // make sure object matches pattern
344 TTreeReaderValue<ldmx::HcalVetoResult> object(
345 events, ("TestObject_" + pass_).c_str());
346 while (events.Next()) {
347 if (object->getMaxPEHit().getID() != header->getEventNumber()) {
348 f->Close();
349 return false;
350 }
351 }
352 } else {
353 // check to make sure object is NOT there
354 auto t{(TTree*)f->Get("LDMX_Events")};
355 if (t and t->GetBranch(("TestObject_" + pass_).c_str())) {
356 f->Close();
357 return false;
358 }
359 }
360
361 TTreeReader runs("LDMX_Run", f);
362
363 if (runs.GetEntries(true) != runs_) {
364 f->Close();
365 return false;
366 }
367
368 TTreeReaderValue<ldmx::RunHeader> run_header(runs, "RunHeader");
369
370 while (runs.Next()) {
371 if (run_header->getRunNumber() !=
372 run_header->getIntParameter("Should Be Run Number")) {
373 f->Close();
374 return false;
375 }
376 }
377
378 f->Close();
379
380 return true;
381 }
382
386 virtual std::string describe() const override {
387 std::ostringstream ss;
388 ss << "can be opened and has the correct number of entries in the event "
389 "tree and the run tree.";
390
391 ss << " TestCollection_" << pass_ << " was verified to ";
393 ss << " be the correct pattern.";
394 else
395 ss << " not be in the file.";
396
397 ss << " TestObject_" << pass_ << " was verified to ";
398 if (exist_object_)
399 ss << " be the correct pattern.";
400 else
401 ss << " not be in the file.";
402
403 return ss.str();
404 }
405
406}; // isGoodEventFile
407
416static bool removeFile(const std::string& filepath) {
417 return remove(filepath.c_str()) == 0;
418}
419
426static void stripRunHeader(const std::string& source,
427 const std::string& destination) {
428 TFile src(source.c_str());
429 REQUIRE(not src.IsZombie());
430 auto events{static_cast<TTree*>(src.Get("LDMX_Events"))};
431 REQUIRE(events != nullptr);
432
433 TFile dst(destination.c_str(), "RECREATE");
434 REQUIRE(dst.IsOpen());
435 dst.cd();
436 auto copy{events->CloneTree(-1, "fast")};
437 REQUIRE(copy != nullptr);
438 copy->Write();
439 dst.Close();
440 src.Close();
441
442 // the point of the exercise
443 TFile check(destination.c_str());
444 REQUIRE(check.Get("LDMX_Run") == nullptr);
445 check.Close();
446}
447
454static bool isCompleted(const std::string& path) {
455 TFile f(path.c_str());
456 REQUIRE(not f.IsZombie());
457 REQUIRE(f.Get("LDMX_Run") != nullptr);
458 TTreeReader runs("LDMX_Run", &f);
459 TTreeReaderValue<ldmx::RunHeader> run_header(runs, "RunHeader");
460 REQUIRE(runs.Next());
461 bool completed{run_header->isCompleted()};
462 f.Close();
463 return completed;
464}
465
469static bool runProcess(const framework::config::Parameters& parameters) {
471 try {
472 p = std::make_unique<Process>(parameters);
474 std::cerr << "Config Error [" << e.name() << "] : " << e.message()
475 << std::endl;
476 std::cerr << " at " << e.module() << ":" << e.line() << " in "
477 << e.function() << std::endl;
478 return false;
479 }
480 p->run();
481 return true;
482}
483
484} // namespace test
485} // namespace framework
486
489
490
523TEST_CASE("Core Framework Functionality", "[Framework][functionality]") {
524 // these parameters aren't tested/changed, so we set them out here
526 process.add("compression_setting", 9);
527 process.add("max_tries_per_event", 1);
528 process.add("log_frequency", -1);
529 process.add("term_log_level", 4);
530 process.add("file_log_level", 4);
531 process.add<std::string>("log_file_name", "");
532 process.add<std::string>("tree_name", "LDMX_Events");
533
534 framework::config::Parameters producer_parameters;
535 producer_parameters.add<std::string>("class_name",
536 "framework::test::TestProducer");
537 producer_parameters.add<std::string>("instance_name", "TestProducer");
538
539 framework::config::Parameters analyzer_parameters;
540 analyzer_parameters.add<std::string>("class_name",
541 "framework::test::TestAnalyzer");
542 analyzer_parameters.add<std::string>("instance_name", "TestAnalyzer");
543
544 // declare used and re-used types, not used in all branches
545 std::vector<framework::config::Parameters> sequence;
546 std::vector<std::string> input_files, output_files;
547
548 SECTION("Production Mode") {
549 // no input files, only output files
550
551 output_files = {"test_productionmode_events.root"};
552 process.add<std::string>("pass_name", "test");
553 process.add("output_files", output_files);
554 process.add("max_events", 3);
555 process.add("run", 3);
556
557 producer_parameters.add("create_run_header", true);
558
559 sequence = {producer_parameters};
560
561 SECTION("only producers") {
562 process.add("sequence", sequence);
563 process.add<std::string>("histogram_file", "");
564 SECTION("no drop/keep rules") {
565 // Process owns and deletes the processors
566 REQUIRE(framework::test::runProcess(process));
567 CHECK_THAT(output_files.at(0),
569 }
570
571 SECTION("drop TestCollection") {
572 std::vector<std::string> keep = {"drop .*Collection.*"};
573 process.add("keep", keep);
574 REQUIRE(framework::test::runProcess(process));
575 CHECK_THAT(output_files.at(0),
576 framework::test::IsGoodEventFile("test", 3, 1, false));
577 }
578
579 SECTION("skim for even indexed events") {
580 process.add("skim_default_is_keep", false);
581 std::vector<std::string> rules = {"TestProducer", ""};
582 process.add("skim_rules", rules);
583 REQUIRE(framework::test::runProcess(process));
584 CHECK_THAT(output_files.at(0),
586 }
587 }
588
589 SECTION("with Analyses") {
590 std::string hist_file_path =
591 "test_productionmode_withanalyses_hists.root";
592
593 process.add("histogram_file", hist_file_path);
594
595 sequence.push_back(analyzer_parameters);
596 process.add("sequence", sequence);
597
598 SECTION("no drop/keep rules") {
599 REQUIRE(framework::test::runProcess(process));
600 CHECK_THAT(output_files.at(0),
602 }
603
604 SECTION("drop TestCollection") {
605 std::vector<std::string> keep = {"drop .*Collection.*"};
606 process.add("keep", keep);
607 REQUIRE(framework::test::runProcess(process));
608 CHECK_THAT(output_files.at(0),
609 framework::test::IsGoodEventFile("test", 3, 1, false));
610 }
611
612 SECTION("skim for even indexed events") {
613 process.add("skim_default_is_keep", false);
614 std::vector<std::string> rules = {"TestProducer", ""};
615 process.add("skim_rules", rules);
616 REQUIRE(framework::test::runProcess(process));
617 CHECK_THAT(output_files.at(0),
619 }
620
621 CHECK_THAT(hist_file_path,
623 CHECK(framework::test::removeFile(hist_file_path));
624 }
625
626 CHECK(framework::test::removeFile(output_files.at(0)));
627 } // Production Mode
628
629 SECTION("Need Input Files") {
630 input_files = {"test_needinputfiles_2_events.root",
631 "test_needinputfiles_3_events.root",
632 "test_needinputfiles_4_events.root"};
633
634 for (int run{2}; run < 5; run++) {
635 auto make_inputs = process;
636 make_inputs.add<std::string>("pass_name", "makeInputs");
637 auto producer = producer_parameters;
638 producer.add("create_run_header", true);
639 make_inputs.add<std::vector<framework::config::Parameters>>("sequence",
640 {producer});
641 output_files = {input_files.at(run - 2)};
642 make_inputs.add("output_files", output_files);
643 make_inputs.add("max_events", run);
644 make_inputs.add("run", run);
645 REQUIRE(framework::test::runProcess(make_inputs));
646 REQUIRE_THAT(input_files.at(run - 2),
647 framework::test::IsGoodEventFile("makeInputs", run, 1));
648 }
649 process.add<std::string>("pass_name", "test");
650
651 SECTION("Analysis Mode") {
652 // no output files, only histogram output
653
654 sequence = {analyzer_parameters};
655 process.add("sequence", sequence);
656
657 std::string hist_file_path = "test_analysismode_hists.root";
658 process.add("histogram_file", hist_file_path);
659
660 SECTION("one input file") {
661 std::vector<std::string> input_file = {input_files.at(0)};
662 process.add("input_files", input_file);
663 REQUIRE(framework::test::runProcess(process));
664 CHECK_THAT(hist_file_path, framework::test::IsGoodhistogramFile(1 + 2));
665 CHECK(framework::test::removeFile(hist_file_path));
666 }
667
668 SECTION("multiple input files") {
669 process.add("input_files", input_files);
670 REQUIRE(framework::test::runProcess(process));
671 CHECK_THAT(hist_file_path, framework::test::IsGoodhistogramFile(
672 1 + 2 + 1 + 2 + 3 + 1 + 2 + 3 + 4));
673 CHECK(framework::test::removeFile(hist_file_path));
674 }
675
676 } // Analysis Mode
677
678 SECTION("Evolve Mode") {
679 // one input file to one output file
680 std::vector<std::string> input_file = {input_files.at(0)};
681 process.add("input_files", input_file);
682
683 std::string event_file_path = "test_evolvemode_events.root";
684 output_files = {event_file_path};
685 process.add("output_files", output_files);
686
687 SECTION("drop TestCollection") {
688 std::vector<std::string> keep = {"drop .*Collection.*"};
689 process.add("keep", keep);
690 REQUIRE(framework::test::runProcess(process));
691 CHECK_THAT(event_file_path,
692 framework::test::IsGoodEventFile("makeInputs", 2, 1, false));
693 }
694 }
695
696 SECTION("Merge Mode") {
697 // many input files to one output file
698
699 process.add("input_files", input_files);
700
701 std::string event_file_path = "test_mergemode_events.root";
702 output_files = {event_file_path};
703 process.add("output_files", output_files);
704
705 SECTION("with analyzers") {
706 sequence = {analyzer_parameters};
707
708 std::string hist_file_path = "test_mergemode_withanalyzers_hists.root";
709
710 process.add("sequence", sequence);
711 process.add("histogram_file", hist_file_path);
712
713 SECTION("no drop/keep rules") {
714 REQUIRE(framework::test::runProcess(process));
715 CHECK_THAT(event_file_path, framework::test::IsGoodEventFile(
716 "makeInputs", 2 + 3 + 4, 3));
717 }
718
719 SECTION("drop TestCollection") {
720 std::vector<std::string> keep = {"drop .*Collection.*"};
721 process.add("keep", keep);
722 REQUIRE(framework::test::runProcess(process));
723 CHECK_THAT(event_file_path, framework::test::IsGoodEventFile(
724 "makeInputs", 2 + 3 + 4, 3, false));
725 }
726
727 CHECK_THAT(hist_file_path, framework::test::IsGoodhistogramFile(
728 1 + 2 + 1 + 2 + 3 + 1 + 2 + 3 + 4));
729 CHECK(framework::test::removeFile(hist_file_path));
730 }
731
732 SECTION("with producers") {
733 producer_parameters.add("create_run_header", false);
734 sequence = {producer_parameters};
735
736 process.add("sequence", sequence);
737
738 SECTION("not listening to storage hints") {
739 REQUIRE(framework::test::runProcess(process));
740 CHECK_THAT(event_file_path,
741 framework::test::IsGoodEventFile("test", 2 + 3 + 4, 3));
742 }
743
744 SECTION("skim for even indexed events") {
745 process.add("skim_default_is_keep", false);
746 std::vector<std::string> rules = {"TestProducer", ""};
747 process.add("skim_rules", rules);
748 REQUIRE(framework::test::runProcess(process));
749 CHECK_THAT(event_file_path,
750 framework::test::IsGoodEventFile("test", 1 + 1 + 2, 3));
751 }
752 }
753
754 CHECK(framework::test::removeFile(event_file_path));
755
756 } // Merge Mode
757
758 } // need input files
759
760} // process test
761
772TEST_CASE("Missing Run Header", "[Framework][functionality]") {
774 process.add("compression_setting", 9);
775 process.add("max_tries_per_event", 1);
776 process.add("log_frequency", -1);
777 process.add("term_log_level", 4);
778 process.add("file_log_level", 4);
779 process.add<std::string>("log_file_name", "");
780 process.add<std::string>("tree_name", "LDMX_Events");
781
782 framework::config::Parameters producer_parameters;
783 producer_parameters.add<std::string>("class_name",
784 "framework::test::TestProducer");
785 producer_parameters.add<std::string>("instance_name", "TestProducer");
786
787 framework::config::Parameters analyzer_parameters;
788 analyzer_parameters.add<std::string>("class_name",
789 "framework::test::TestAnalyzer");
790 analyzer_parameters.add<std::string>("instance_name", "TestAnalyzer");
791
792 const std::string healthy_file{"test_missingrh_healthy_events.root"};
793 const std::string headerless_file{"test_missingrh_headerless_events.root"};
794
795 // healthy file, then take its run header away
796 {
797 auto make_inputs = process;
798 make_inputs.add<std::string>("pass_name", "makeInputs");
799 auto producer = producer_parameters;
800 producer.add("create_run_header", true);
801 make_inputs.add<std::vector<framework::config::Parameters>>("sequence",
802 {producer});
803 make_inputs.add("output_files", std::vector<std::string>{healthy_file});
804 make_inputs.add<std::string>("histogram_file", "");
805 make_inputs.add("max_events", 3);
806 make_inputs.add("run", 3);
807 REQUIRE(framework::test::runProcess(make_inputs));
808 REQUIRE_THAT(healthy_file,
809 framework::test::IsGoodEventFile("makeInputs", 3, 1));
810 }
811 framework::test::stripRunHeader(healthy_file, headerless_file);
812
813 process.add<std::string>("pass_name", "test");
814 std::string hist_file_path = "test_missingrh_hists.root";
815 process.add("histogram_file", hist_file_path);
816 process.add<std::vector<framework::config::Parameters>>(
817 "sequence", {analyzer_parameters});
818 process.add("input_files", std::vector<std::string>{headerless_file});
819
820 CHECK_THROWS_AS(framework::test::runProcess(process),
822
823 CHECK(framework::test::removeFile(hist_file_path));
824 CHECK(framework::test::removeFile(headerless_file));
825 CHECK(framework::test::removeFile(healthy_file));
826} // missing run header test
827
838TEST_CASE("Output Completeness", "[Framework][functionality]") {
840 process.add("compression_setting", 9);
841 process.add("max_tries_per_event", 1);
842 process.add("log_frequency", -1);
843 process.add("term_log_level", 4);
844 process.add("file_log_level", 4);
845 process.add<std::string>("log_file_name", "");
846 process.add<std::string>("tree_name", "LDMX_Events");
847 process.add<std::string>("pass_name", "test");
848 process.add<std::string>("histogram_file", "");
849 process.add("max_events", 5);
850 process.add("run", 3);
851
852 framework::config::Parameters producer_parameters;
853 producer_parameters.add<std::string>("class_name",
854 "framework::test::TestProducer");
855 producer_parameters.add<std::string>("instance_name", "TestProducer");
856 producer_parameters.add("create_run_header", true);
857
858 const std::string output_file{"test_completeness_events.root"};
859 process.add("output_files", std::vector<std::string>{output_file});
860
861 SECTION("a clean close is marked completed") {
862 process.add<std::vector<framework::config::Parameters>>(
863 "sequence", {producer_parameters});
864 REQUIRE(framework::test::runProcess(process));
865 CHECK(framework::test::isCompleted(output_file));
866 CHECK_THAT(output_file, framework::test::IsGoodEventFile("test", 5, 1));
867 }
868
869 SECTION("an interrupted run is not") {
870 // die partway, as a preemption would
871 producer_parameters.add("throw_after", 3);
872 process.add<std::vector<framework::config::Parameters>>(
873 "sequence", {producer_parameters});
874 CHECK_THROWS_AS(framework::test::runProcess(process),
876 // present because it was written at open, not on close
877 CHECK_FALSE(framework::test::isCompleted(output_file));
878 // and it carries what the producer put in it
879 CHECK_THAT(output_file, framework::test::IsGoodEventFile("test", 3, 1));
880 }
881
882 CHECK(framework::test::removeFile(output_file));
883} // output completeness test
884
896TEST_CASE("Incomplete Input", "[Framework][functionality]") {
898 process.add("compression_setting", 9);
899 process.add("max_tries_per_event", 1);
900 process.add("log_frequency", -1);
901 process.add("term_log_level", 4);
902 process.add("file_log_level", 4);
903 process.add<std::string>("log_file_name", "");
904 process.add<std::string>("tree_name", "LDMX_Events");
905
906 framework::config::Parameters producer_parameters;
907 producer_parameters.add<std::string>("class_name",
908 "framework::test::TestProducer");
909 producer_parameters.add<std::string>("instance_name", "TestProducer");
910 producer_parameters.add("create_run_header", true);
911
912 framework::config::Parameters analyzer_parameters;
913 analyzer_parameters.add<std::string>("class_name",
914 "framework::test::TestAnalyzer");
915 analyzer_parameters.add<std::string>("instance_name", "TestAnalyzer");
916
917 const std::string complete_file{"test_incompletein_complete_events.root"};
918 const std::string incomplete_file{"test_incompletein_incomplete_events.root"};
919
920 // one file that closes cleanly and one that dies partway
921 {
922 auto make_inputs = process;
923 make_inputs.add<std::string>("pass_name", "makeInputs");
924 make_inputs.add<std::vector<framework::config::Parameters>>(
925 "sequence", {producer_parameters});
926 make_inputs.add("output_files", std::vector<std::string>{complete_file});
927 make_inputs.add<std::string>("histogram_file", "");
928 make_inputs.add("max_events", 3);
929 make_inputs.add("run", 3);
930 REQUIRE(framework::test::runProcess(make_inputs));
931 REQUIRE(framework::test::isCompleted(complete_file));
932 }
933 {
934 auto make_inputs = process;
935 make_inputs.add<std::string>("pass_name", "makeInputs");
936 auto producer = producer_parameters;
937 producer.add("throw_after", 3); // die partway, as a preemption would
938 make_inputs.add<std::vector<framework::config::Parameters>>("sequence",
939 {producer});
940 make_inputs.add("output_files", std::vector<std::string>{incomplete_file});
941 make_inputs.add<std::string>("histogram_file", "");
942 make_inputs.add("max_events", 5);
943 make_inputs.add("run", 4);
944 REQUIRE_THROWS_AS(framework::test::runProcess(make_inputs),
946 REQUIRE_FALSE(framework::test::isCompleted(incomplete_file));
947 }
948
949 process.add<std::string>("pass_name", "test");
950 std::string hist_file_path = "test_incompletein_hists.root";
951 process.add("histogram_file", hist_file_path);
952 process.add<std::vector<framework::config::Parameters>>(
953 "sequence", {analyzer_parameters});
954
955 SECTION("an unfinished run is refused") {
956 process.add("input_files", std::vector<std::string>{incomplete_file});
957 CHECK_THROWS_AS(framework::test::runProcess(process),
959 }
960
961 SECTION("an unfinished run is refused even alongside good files") {
962 process.add("input_files",
963 std::vector<std::string>{complete_file, incomplete_file});
964 CHECK_THROWS_AS(framework::test::runProcess(process),
966 }
967
968 SECTION("experts can ask for it anyway") {
969 process.add("input_files", std::vector<std::string>{incomplete_file});
970 process.add("allow_incomplete_input_files", true);
971 CHECK(framework::test::runProcess(process));
972 CHECK_THAT(hist_file_path, framework::test::IsGoodhistogramFile(1 + 2 + 3));
973 }
974
975 SECTION("a complete run is let through") {
976 process.add("input_files", std::vector<std::string>{complete_file});
977 CHECK(framework::test::runProcess(process));
978 CHECK_THAT(hist_file_path, framework::test::IsGoodhistogramFile(1 + 2 + 3));
979 }
980
981 CHECK(framework::test::removeFile(hist_file_path));
982 CHECK(framework::test::removeFile(incomplete_file));
983 CHECK(framework::test::removeFile(complete_file));
984} // incomplete input test
Class that represents a reconstructed hit in a calorimeter cell within the detector.
Base classes for all user event processing components to extend.
#define DECLARE_ANALYZER(CLASS)
Macro which allows the framework to construct an analyzer given its name during configuration.
#define DECLARE_PRODUCER(CLASS)
Macro which allows the framework to construct a producer given its name during configuration.
Class that stores Stores reconstructed hit information from the HCAL.
Class used to encapsulate the results obtained from HcalVetoProcessor.
Class which represents the process under execution.
Base class for a module which does not produce a data product.
Analyzer(const std::string &name, Process &process)
Class constructor.
TDirectory * getHistoDirectory()
Access/create a directory in the histogram file for this event processor to create histograms and ana...
void setStorageHint(framework::StorageControl::Hint hint)
Mark the current event as having the given storage control hint from this module_.
Implements an event buffer system for storing event data.
Definition Event.h:40
Class which represents the process under execution.
Definition Process.h:34
Base class for a module which produces a data product.
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:26
void add(const std::string &name, const T &value)
Add a parameter to the parameter list.
Definition Parameters.h:39
const T & get(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:75
Standard base exception class with some useful output information.
Definition Exception.h:20
const std::string & function() const
Get the function name where the exception occurred.
Definition Exception.h:74
int line() const
Get the source line number where the exception occurred.
Definition Exception.h:80
const std::string & message() const
Get the message of the exception.
Definition Exception.h:62
const std::string & name() const
Get the name of the exception.
Definition Exception.h:56
const std::string & module() const
Get the source filename where the exception occurred.
Definition Exception.h:68
int entries_
correct number of entries in the event ttree
bool exist_object_
object should exist in file
std::string pass_
pass name to check the collection and/or object for
bool match(const std::string &filename) const override
Actually do the matching.
IsGoodEventFile(const std::string &pass, const int &entries, const int &runs, bool existColl=true, bool existObj=true)
Constructor.
bool exist_collection_
collection should exist in file
virtual std::string describe() const override
Human-readable statement for any match that is true.
bool match(const std::string &filename) const override
Performs the test for this matcher.
int correct_get_entries_
Correct number of entries.
virtual std::string describe() const override
Describe this matcher in a helpful, human-readable way.
IsGoodhistogramFile(int const &n)
Constructor.
Bare analyzer that looks for objects matching what the TestProducer put in.
void configure(framework::config::Parameters &ps) override
Callback for the EventProcessor to configure itself from the given set of parameters.
void analyze(const framework::Event &event) final override
Process the event and make histograms or summaries.
TH1F * test_hist_
test histogram filled with event indices
void onProcessStart() final override
Callback for the EventProcessor to take any necessary action when the processing of events starts,...
Bare producer that creates a collection and an object and puts them on the event bus.
int events_
number of events we've gotten to
bool create_run_header_
should we create the run header?
void configure(framework::config::Parameters &p) final override
Callback for the EventProcessor to configure itself from the given set of parameters.
int throw_after_
throw after this many events (negative to never throw)
void produce(framework::Event &event) final override
Process the event and put new data products into it.
void beforeNewRun(ldmx::RunHeader &header) final override
Callback for Producers to add parameters to the run header before conditions are initialized.
Represents a reconstructed hit in a calorimeter cell within the detector.
void setID(int id)
Set the detector ID.
Stores reconstructed hit information from the HCAL.
Definition HcalHit.h:24
void setVetoResult(const bool &passes_veto=true)
Sets whether the Hcal veto was passed or not.
bool passesVeto() const
Checks if the event passes the Hcal veto.
ldmx::HcalHit getMaxPEHit() const
void setMaxPEHit(const ldmx::HcalHit max_PE_hit)
Set the maximum PE hit.
Run-specific configuration and data stored in its own output TTree alongside the event TTree in the o...
Definition RunHeader.h:68
Parameters run(const std::string &root_object, const std::string &pythonScript, char *args[], int nargs)
run the python script and extract the parameters
Definition Python.cxx:300
All classes in the ldmx-sw project use this namespace.
std::unique_ptr< Process > ProcessHandle
A handle to the current process Used to pass a process from ConfigurePython to fire....
Definition Process.h:243