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#include <cstdio> //for remove
5
6#include "Framework/EventFile.h"
8#include "Framework/Process.h"
9#include "Framework/RunHeader.h"
10#include "Hcal/Event/HcalHit.h"
13#include "TFile.h" //to open and check root files
14#include "TH1F.h" //for test histogram
15#include "TTreeReader.h" //to check output event files
16
17using Catch::Approx;
18
19namespace framework {
20namespace test {
21
42class TestProducer : public Producer {
45
48
51
52 public:
53 TestProducer(const std::string& name, Process& p) : Producer(name, p) {}
54 ~TestProducer() {}
55
56 void configure(framework::config::Parameters& p) final override {
57 create_run_header_ = p.get<bool>("create_run_header");
58 throw_after_ = p.get<int>("throw_after", -1);
59 }
60
61 void beforeNewRun(ldmx::RunHeader& header) final override {
62 if (not create_run_header_) return;
63 header.setIntParameter("Should Be Run Number", header.getRunNumber());
64 return;
65 }
66
67 void produce(framework::Event& event) final override {
68 int i_event = event.getEventNumber();
69
70 REQUIRE(i_event > 0);
71
72 // stand-in for a kill mid-write
73 if (throw_after_ > 0 and i_event > throw_after_) {
74 EXCEPTION_RAISE("TestKill", "Pretending to be killed mid-run.");
75 }
76
77 std::vector<ldmx::CalorimeterHit> calo_hits;
78 for (int i = 0; i < i_event; i++) {
79 calo_hits.emplace_back();
80 calo_hits.back().setID(i_event * 10 + i);
81 }
82
83 REQUIRE_NOTHROW(event.add("TestCollection", calo_hits));
84
85 ldmx::HcalHit max_pe_hit;
86 max_pe_hit.setID(i_event);
87
89 res.setMaxPEHit(max_pe_hit);
90 res.setVetoResult(i_event % 2 == 0);
91
92 REQUIRE_NOTHROW(event.add("TestObject", res));
93
94 events_ = i_event;
95
96 std::vector<int> event_indices = {i_event, i_event};
97 REQUIRE_NOTHROW(event.add("EventIndex", event_indices));
98
99 float test_float = i_event * 0.1;
100 REQUIRE_NOTHROW(event.add("EventTenth", test_float));
101
102 if (res.passesVeto()) setStorageHint(StorageControl::Hint::MustKeep);
103
104 return;
105 }
106}; // TestProducer
107
117class TestAnalyzer : public Analyzer {
118 private:
119 std::string test_collection_passname_;
120 std::string test_object_passname_;
121 std::string veto_test_object_passname_;
122 std::string tenth_event_passname_;
123 std::string event_index_passname_;
124
125 public:
126 TestAnalyzer(const std::string& name, Process& p) : Analyzer(name, p) {}
127 ~TestAnalyzer() {}
128
130 test_collection_passname_ =
131 ps.get<std::string>("test_collection_passname", "");
132 test_object_passname_ = ps.get<std::string>("test_object_passname", "");
133 veto_test_object_passname_ =
134 ps.get<std::string>("veto_test_object_passname", "");
135 tenth_event_passname_ = ps.get<std::string>("tenth_event_passname", "");
136 event_index_passname_ = ps.get<std::string>("event_index_passname", "");
137 }
138
139 void onProcessStart() final override {
140 REQUIRE_NOTHROW(getHistoDirectory());
141 test_hist_ = new TH1F("test_hist_", "Test Histogram", 101, -50, 50);
142 test_hist_->SetCanExtend(TH1::kAllAxes);
143 }
144
145 void analyze(const framework::Event& event) final override {
146 int i_event = event.getEventNumber();
147
148 REQUIRE(i_event > 0);
149
150 const std::vector<ldmx::CalorimeterHit>& calo_hits =
151 event.getCollection<ldmx::CalorimeterHit>("TestCollection",
152 test_collection_passname_);
153
154 CHECK(calo_hits.size() == i_event);
155 for (unsigned int i = 0; i < calo_hits.size(); i++) {
156 CHECK(calo_hits.at(i).getID() == i_event * 10 + i);
157 test_hist_->Fill(calo_hits.at(i).getID());
158 }
159
160 const ldmx::HcalVetoResult& veto_res =
161 event.getObject<ldmx::HcalVetoResult>("TestObject",
162 veto_test_object_passname_);
163
164 auto max_pe_hit{veto_res.getMaxPEHit()};
165
166 CHECK(max_pe_hit.getID() == i_event);
167 CHECK(veto_res.passesVeto() == (i_event % 2 == 0));
168
169 const float& tenth_event =
170 event.getObject<float>("EventTenth", tenth_event_passname_);
171 CHECK(tenth_event == Approx(i_event * 0.1));
172
173 const std::vector<int>& i_event_from_bus =
174 event.getCollection<int>("EventIndex", event_index_passname_);
175
176 CHECK(i_event_from_bus.size() == 2);
177 CHECK(i_event_from_bus.at(0) == i_event);
178 CHECK(i_event_from_bus.at(1) == i_event);
179
180 return;
181 }
182
183 private:
186}; // TestAnalyzer
187
203class IsGoodhistogramFile : public Catch::Matchers::MatcherBase<std::string> {
204 private:
207
208 public:
215
223 bool match(const std::string& filename) const override {
224 // Open file
225 TFile* f = TFile::Open(filename.c_str());
226 if (!f) return false;
227 TDirectory* d = (TDirectory*)f->Get("TestAnalyzer");
228 if (!d) return false;
229 TH1F* h = (TH1F*)d->Get("test_hist_");
230 if (!h) return false;
231
232 return (h->GetEntries() == correct_get_entries_);
233 }
234
241 virtual std::string describe() const override {
242 std::ostringstream ss;
243 ss << "has the histogram 'TestAnalyzer/test_hist_' with the number of "
244 "entries "
246 return ss.str();
247 }
248}; // isGoodhistogram_file
249
265class IsGoodEventFile : public Catch::Matchers::MatcherBase<std::string> {
266 private:
268 std::string pass_;
269
272
274 int runs_;
275
278
281
282 public:
288 IsGoodEventFile(const std::string& pass, const int& entries, const int& runs,
289 bool existColl = true, bool existObj = true)
290 : pass_(pass),
291 entries_(entries),
292 runs_(runs),
293 exist_collection_(existColl),
294 exist_object_(existObj) {}
295
304 bool match(const std::string& filename) const override {
305 TFile* f = TFile::Open(filename.c_str());
306 if (!f) return false;
307
308 TTreeReader events("LDMX_Events", f);
309
310 if (events.GetEntries(true) != entries_) {
311 f->Close();
312 return false;
313 }
314
315 // Event tree should _always_ have the ldmx::EventHeader
316 TTreeReaderValue<ldmx::EventHeader> header(events, "EventHeader");
317
318 if (exist_collection_) {
319 // make sure collection matches pattern
320 TTreeReaderValue<std::vector<ldmx::CalorimeterHit>> collection(
321 events, ("TestCollection_" + pass_).c_str());
322 while (events.Next()) {
323 if (collection->size() != header->getEventNumber()) {
324 f->Close();
325 return false;
326 }
327 for (unsigned int i = 0; i < collection->size(); i++)
328 if (collection->at(i).getID() != header->getEventNumber() * 10 + i) {
329 f->Close();
330 return false;
331 }
332 }
333 // restart in case checking object as well
334 events.Restart();
335 } else {
336 // check to make sure collection is NOT there
337 auto t{(TTree*)f->Get("LDMX_Events")};
338 if (t and t->GetBranch(("TestCollection_" + pass_).c_str())) {
339 f->Close();
340 return false;
341 }
342 }
343
344 if (exist_object_) {
345 // make sure object matches pattern
346 TTreeReaderValue<ldmx::HcalVetoResult> object(
347 events, ("TestObject_" + pass_).c_str());
348 while (events.Next()) {
349 if (object->getMaxPEHit().getID() != header->getEventNumber()) {
350 f->Close();
351 return false;
352 }
353 }
354 } else {
355 // check to make sure object is NOT there
356 auto t{(TTree*)f->Get("LDMX_Events")};
357 if (t and t->GetBranch(("TestObject_" + pass_).c_str())) {
358 f->Close();
359 return false;
360 }
361 }
362
363 TTreeReader runs("LDMX_Run", f);
364
365 if (runs.GetEntries(true) != runs_) {
366 f->Close();
367 return false;
368 }
369
370 TTreeReaderValue<ldmx::RunHeader> run_header(runs, "RunHeader");
371
372 while (runs.Next()) {
373 if (run_header->getRunNumber() !=
374 run_header->getIntParameter("Should Be Run Number")) {
375 f->Close();
376 return false;
377 }
378 }
379
380 f->Close();
381
382 return true;
383 }
384
388 virtual std::string describe() const override {
389 std::ostringstream ss;
390 ss << "can be opened and has the correct number of entries in the event "
391 "tree and the run tree.";
392
393 ss << " TestCollection_" << pass_ << " was verified to ";
395 ss << " be the correct pattern.";
396 else
397 ss << " not be in the file.";
398
399 ss << " TestObject_" << pass_ << " was verified to ";
400 if (exist_object_)
401 ss << " be the correct pattern.";
402 else
403 ss << " not be in the file.";
404
405 return ss.str();
406 }
407
408}; // isGoodEventFile
409
418static bool removeFile(const std::string& filepath) {
419 return remove(filepath.c_str()) == 0;
420}
421
428static void stripRunHeader(const std::string& source,
429 const std::string& destination) {
430 TFile src(source.c_str());
431 REQUIRE(not src.IsZombie());
432 auto events{static_cast<TTree*>(src.Get("LDMX_Events"))};
433 REQUIRE(events != nullptr);
434
435 TFile dst(destination.c_str(), "RECREATE");
436 REQUIRE(dst.IsOpen());
437 dst.cd();
438 auto copy{events->CloneTree(-1, "fast")};
439 REQUIRE(copy != nullptr);
440 copy->Write();
441 dst.Close();
442 src.Close();
443
444 // the point of the exercise
445 TFile check(destination.c_str());
446 REQUIRE(check.Get("LDMX_Run") == nullptr);
447 check.Close();
448}
449
456static bool isCompleted(const std::string& path) {
457 TFile f(path.c_str());
458 REQUIRE(not f.IsZombie());
459 REQUIRE(f.Get("LDMX_Run") != nullptr);
460 TTreeReader runs("LDMX_Run", &f);
461 TTreeReaderValue<ldmx::RunHeader> run_header(runs, "RunHeader");
462 REQUIRE(runs.Next());
463 bool completed{run_header->isCompleted()};
464 f.Close();
465 return completed;
466}
467
471static bool runProcess(const framework::config::Parameters& parameters) {
473 try {
474 p = std::make_unique<Process>(parameters);
476 std::cerr << "Config Error [" << e.name() << "] : " << e.message()
477 << std::endl;
478 std::cerr << " at " << e.module() << ":" << e.line() << " in "
479 << e.function() << std::endl;
480 return false;
481 }
482 p->run();
483 return true;
484}
485
486} // namespace test
487} // namespace framework
488
491
492
525TEST_CASE("Core Framework Functionality", "[Framework][functionality]") {
526 // these parameters aren't tested/changed, so we set them out here
528 process.add("compression_setting", 9);
529 process.add("max_tries_per_event", 1);
530 process.add("log_frequency", -1);
531 process.add("term_log_level", 4);
532 process.add("file_log_level", 4);
533 process.add<std::string>("log_file_name", "");
534 process.add<std::string>("tree_name", "LDMX_Events");
535
536 framework::config::Parameters producer_parameters;
537 producer_parameters.add<std::string>("class_name",
538 "framework::test::TestProducer");
539 producer_parameters.add<std::string>("instance_name", "TestProducer");
540
541 framework::config::Parameters analyzer_parameters;
542 analyzer_parameters.add<std::string>("class_name",
543 "framework::test::TestAnalyzer");
544 analyzer_parameters.add<std::string>("instance_name", "TestAnalyzer");
545
546 // declare used and re-used types, not used in all branches
547 std::vector<framework::config::Parameters> sequence;
548 std::vector<std::string> input_files, output_files;
549
550 SECTION("Production Mode") {
551 // no input files, only output files
552
553 output_files = {"test_productionmode_events.root"};
554 process.add<std::string>("pass_name", "test");
555 process.add("output_files", output_files);
556 process.add("max_events", 3);
557 process.add("run", 3);
558
559 producer_parameters.add("create_run_header", true);
560
561 sequence = {producer_parameters};
562
563 SECTION("only producers") {
564 process.add("sequence", sequence);
565 process.add<std::string>("histogram_file", "");
566 SECTION("no drop/keep rules") {
567 // Process owns and deletes the processors
568 REQUIRE(framework::test::runProcess(process));
569 CHECK_THAT(output_files.at(0),
571 }
572
573 SECTION("drop TestCollection") {
574 std::vector<std::string> keep = {"drop .*Collection.*"};
575 process.add("keep", keep);
576 REQUIRE(framework::test::runProcess(process));
577 CHECK_THAT(output_files.at(0),
578 framework::test::IsGoodEventFile("test", 3, 1, false));
579 }
580
581 SECTION("skim for even indexed events") {
582 process.add("skim_default_is_keep", false);
583 std::vector<std::string> rules = {"TestProducer", ""};
584 process.add("skim_rules", rules);
585 REQUIRE(framework::test::runProcess(process));
586 CHECK_THAT(output_files.at(0),
588 }
589 }
590
591 SECTION("with Analyses") {
592 std::string hist_file_path =
593 "test_productionmode_withanalyses_hists.root";
594
595 process.add("histogram_file", hist_file_path);
596
597 sequence.push_back(analyzer_parameters);
598 process.add("sequence", sequence);
599
600 SECTION("no drop/keep rules") {
601 REQUIRE(framework::test::runProcess(process));
602 CHECK_THAT(output_files.at(0),
604 }
605
606 SECTION("drop TestCollection") {
607 std::vector<std::string> keep = {"drop .*Collection.*"};
608 process.add("keep", keep);
609 REQUIRE(framework::test::runProcess(process));
610 CHECK_THAT(output_files.at(0),
611 framework::test::IsGoodEventFile("test", 3, 1, false));
612 }
613
614 SECTION("skim for even indexed events") {
615 process.add("skim_default_is_keep", false);
616 std::vector<std::string> rules = {"TestProducer", ""};
617 process.add("skim_rules", rules);
618 REQUIRE(framework::test::runProcess(process));
619 CHECK_THAT(output_files.at(0),
621 }
622
623 CHECK_THAT(hist_file_path,
625 CHECK(framework::test::removeFile(hist_file_path));
626 }
627
628 CHECK(framework::test::removeFile(output_files.at(0)));
629 } // Production Mode
630
631 SECTION("Need Input Files") {
632 input_files = {"test_needinputfiles_2_events.root",
633 "test_needinputfiles_3_events.root",
634 "test_needinputfiles_4_events.root"};
635
636 for (int run{2}; run < 5; run++) {
637 auto make_inputs = process;
638 make_inputs.add<std::string>("pass_name", "makeInputs");
639 auto producer = producer_parameters;
640 producer.add("create_run_header", true);
641 make_inputs.add<std::vector<framework::config::Parameters>>("sequence",
642 {producer});
643 output_files = {input_files.at(run - 2)};
644 make_inputs.add("output_files", output_files);
645 make_inputs.add("max_events", run);
646 make_inputs.add("run", run);
647 REQUIRE(framework::test::runProcess(make_inputs));
648 REQUIRE_THAT(input_files.at(run - 2),
649 framework::test::IsGoodEventFile("makeInputs", run, 1));
650 }
651 process.add<std::string>("pass_name", "test");
652
653 SECTION("Analysis Mode") {
654 // no output files, only histogram output
655
656 sequence = {analyzer_parameters};
657 process.add("sequence", sequence);
658
659 std::string hist_file_path = "test_analysismode_hists.root";
660 process.add("histogram_file", hist_file_path);
661
662 SECTION("one input file") {
663 std::vector<std::string> input_file = {input_files.at(0)};
664 process.add("input_files", input_file);
665 REQUIRE(framework::test::runProcess(process));
666 CHECK_THAT(hist_file_path, framework::test::IsGoodhistogramFile(1 + 2));
667 CHECK(framework::test::removeFile(hist_file_path));
668 }
669
670 SECTION("multiple input files") {
671 process.add("input_files", input_files);
672 REQUIRE(framework::test::runProcess(process));
673 CHECK_THAT(hist_file_path, framework::test::IsGoodhistogramFile(
674 1 + 2 + 1 + 2 + 3 + 1 + 2 + 3 + 4));
675 CHECK(framework::test::removeFile(hist_file_path));
676 }
677
678 } // Analysis Mode
679
680 SECTION("Evolve Mode") {
681 // one input file to one output file
682 std::vector<std::string> input_file = {input_files.at(0)};
683 process.add("input_files", input_file);
684
685 std::string event_file_path = "test_evolvemode_events.root";
686 output_files = {event_file_path};
687 process.add("output_files", output_files);
688
689 SECTION("drop TestCollection") {
690 std::vector<std::string> keep = {"drop .*Collection.*"};
691 process.add("keep", keep);
692 REQUIRE(framework::test::runProcess(process));
693 CHECK_THAT(event_file_path,
694 framework::test::IsGoodEventFile("makeInputs", 2, 1, false));
695 }
696 }
697
698 SECTION("Merge Mode") {
699 // many input files to one output file
700
701 process.add("input_files", input_files);
702
703 std::string event_file_path = "test_mergemode_events.root";
704 output_files = {event_file_path};
705 process.add("output_files", output_files);
706
707 SECTION("with analyzers") {
708 sequence = {analyzer_parameters};
709
710 std::string hist_file_path = "test_mergemode_withanalyzers_hists.root";
711
712 process.add("sequence", sequence);
713 process.add("histogram_file", hist_file_path);
714
715 SECTION("no drop/keep rules") {
716 REQUIRE(framework::test::runProcess(process));
717 CHECK_THAT(event_file_path, framework::test::IsGoodEventFile(
718 "makeInputs", 2 + 3 + 4, 3));
719 }
720
721 SECTION("drop TestCollection") {
722 std::vector<std::string> keep = {"drop .*Collection.*"};
723 process.add("keep", keep);
724 REQUIRE(framework::test::runProcess(process));
725 CHECK_THAT(event_file_path, framework::test::IsGoodEventFile(
726 "makeInputs", 2 + 3 + 4, 3, false));
727 }
728
729 CHECK_THAT(hist_file_path, framework::test::IsGoodhistogramFile(
730 1 + 2 + 1 + 2 + 3 + 1 + 2 + 3 + 4));
731 CHECK(framework::test::removeFile(hist_file_path));
732 }
733
734 SECTION("with producers") {
735 producer_parameters.add("create_run_header", false);
736 sequence = {producer_parameters};
737
738 process.add("sequence", sequence);
739
740 SECTION("not listening to storage hints") {
741 REQUIRE(framework::test::runProcess(process));
742 CHECK_THAT(event_file_path,
743 framework::test::IsGoodEventFile("test", 2 + 3 + 4, 3));
744 }
745
746 SECTION("skim for even indexed events") {
747 process.add("skim_default_is_keep", false);
748 std::vector<std::string> rules = {"TestProducer", ""};
749 process.add("skim_rules", rules);
750 REQUIRE(framework::test::runProcess(process));
751 CHECK_THAT(event_file_path,
752 framework::test::IsGoodEventFile("test", 1 + 1 + 2, 3));
753 }
754 }
755
756 CHECK(framework::test::removeFile(event_file_path));
757
758 } // Merge Mode
759
760 } // need input files
761
762} // process test
763
774TEST_CASE("Missing Run Header", "[Framework][functionality]") {
776 process.add("compression_setting", 9);
777 process.add("max_tries_per_event", 1);
778 process.add("log_frequency", -1);
779 process.add("term_log_level", 4);
780 process.add("file_log_level", 4);
781 process.add<std::string>("log_file_name", "");
782 process.add<std::string>("tree_name", "LDMX_Events");
783
784 framework::config::Parameters producer_parameters;
785 producer_parameters.add<std::string>("class_name",
786 "framework::test::TestProducer");
787 producer_parameters.add<std::string>("instance_name", "TestProducer");
788
789 framework::config::Parameters analyzer_parameters;
790 analyzer_parameters.add<std::string>("class_name",
791 "framework::test::TestAnalyzer");
792 analyzer_parameters.add<std::string>("instance_name", "TestAnalyzer");
793
794 const std::string healthy_file{"test_missingrh_healthy_events.root"};
795 const std::string headerless_file{"test_missingrh_headerless_events.root"};
796
797 // healthy file, then take its run header away
798 {
799 auto make_inputs = process;
800 make_inputs.add<std::string>("pass_name", "makeInputs");
801 auto producer = producer_parameters;
802 producer.add("create_run_header", true);
803 make_inputs.add<std::vector<framework::config::Parameters>>("sequence",
804 {producer});
805 make_inputs.add("output_files", std::vector<std::string>{healthy_file});
806 make_inputs.add<std::string>("histogram_file", "");
807 make_inputs.add("max_events", 3);
808 make_inputs.add("run", 3);
809 REQUIRE(framework::test::runProcess(make_inputs));
810 REQUIRE_THAT(healthy_file,
811 framework::test::IsGoodEventFile("makeInputs", 3, 1));
812 }
813 framework::test::stripRunHeader(healthy_file, headerless_file);
814
815 process.add<std::string>("pass_name", "test");
816 std::string hist_file_path = "test_missingrh_hists.root";
817 process.add("histogram_file", hist_file_path);
818 process.add<std::vector<framework::config::Parameters>>(
819 "sequence", {analyzer_parameters});
820 process.add("input_files", std::vector<std::string>{headerless_file});
821
822 CHECK_THROWS_AS(framework::test::runProcess(process),
824
825 CHECK(framework::test::removeFile(hist_file_path));
826 CHECK(framework::test::removeFile(headerless_file));
827 CHECK(framework::test::removeFile(healthy_file));
828} // missing run header test
829
840TEST_CASE("Output Completeness", "[Framework][functionality]") {
842 process.add("compression_setting", 9);
843 process.add("max_tries_per_event", 1);
844 process.add("log_frequency", -1);
845 process.add("term_log_level", 4);
846 process.add("file_log_level", 4);
847 process.add<std::string>("log_file_name", "");
848 process.add<std::string>("tree_name", "LDMX_Events");
849 process.add<std::string>("pass_name", "test");
850 process.add<std::string>("histogram_file", "");
851 process.add("max_events", 5);
852 process.add("run", 3);
853
854 framework::config::Parameters producer_parameters;
855 producer_parameters.add<std::string>("class_name",
856 "framework::test::TestProducer");
857 producer_parameters.add<std::string>("instance_name", "TestProducer");
858 producer_parameters.add("create_run_header", true);
859
860 const std::string output_file{"test_completeness_events.root"};
861 process.add("output_files", std::vector<std::string>{output_file});
862
863 SECTION("a clean close is marked completed") {
864 process.add<std::vector<framework::config::Parameters>>(
865 "sequence", {producer_parameters});
866 REQUIRE(framework::test::runProcess(process));
867 CHECK(framework::test::isCompleted(output_file));
868 CHECK_THAT(output_file, framework::test::IsGoodEventFile("test", 5, 1));
869 }
870
871 SECTION("an interrupted run is not") {
872 // die partway, as a preemption would
873 producer_parameters.add("throw_after", 3);
874 process.add<std::vector<framework::config::Parameters>>(
875 "sequence", {producer_parameters});
876 CHECK_THROWS_AS(framework::test::runProcess(process),
878 // present because it was written at open, not on close
879 CHECK_FALSE(framework::test::isCompleted(output_file));
880 // and it carries what the producer put in it
881 CHECK_THAT(output_file, framework::test::IsGoodEventFile("test", 3, 1));
882 }
883
884 CHECK(framework::test::removeFile(output_file));
885} // output completeness test
886
898TEST_CASE("Incomplete Input", "[Framework][functionality]") {
900 process.add("compression_setting", 9);
901 process.add("max_tries_per_event", 1);
902 process.add("log_frequency", -1);
903 process.add("term_log_level", 4);
904 process.add("file_log_level", 4);
905 process.add<std::string>("log_file_name", "");
906 process.add<std::string>("tree_name", "LDMX_Events");
907
908 framework::config::Parameters producer_parameters;
909 producer_parameters.add<std::string>("class_name",
910 "framework::test::TestProducer");
911 producer_parameters.add<std::string>("instance_name", "TestProducer");
912 producer_parameters.add("create_run_header", true);
913
914 framework::config::Parameters analyzer_parameters;
915 analyzer_parameters.add<std::string>("class_name",
916 "framework::test::TestAnalyzer");
917 analyzer_parameters.add<std::string>("instance_name", "TestAnalyzer");
918
919 const std::string complete_file{"test_incompletein_complete_events.root"};
920 const std::string incomplete_file{"test_incompletein_incomplete_events.root"};
921
922 // one file that closes cleanly and one that dies partway
923 {
924 auto make_inputs = process;
925 make_inputs.add<std::string>("pass_name", "makeInputs");
926 make_inputs.add<std::vector<framework::config::Parameters>>(
927 "sequence", {producer_parameters});
928 make_inputs.add("output_files", std::vector<std::string>{complete_file});
929 make_inputs.add<std::string>("histogram_file", "");
930 make_inputs.add("max_events", 3);
931 make_inputs.add("run", 3);
932 REQUIRE(framework::test::runProcess(make_inputs));
933 REQUIRE(framework::test::isCompleted(complete_file));
934 }
935 {
936 auto make_inputs = process;
937 make_inputs.add<std::string>("pass_name", "makeInputs");
938 auto producer = producer_parameters;
939 producer.add("throw_after", 3); // die partway, as a preemption would
940 make_inputs.add<std::vector<framework::config::Parameters>>("sequence",
941 {producer});
942 make_inputs.add("output_files", std::vector<std::string>{incomplete_file});
943 make_inputs.add<std::string>("histogram_file", "");
944 make_inputs.add("max_events", 5);
945 make_inputs.add("run", 4);
946 REQUIRE_THROWS_AS(framework::test::runProcess(make_inputs),
948 REQUIRE_FALSE(framework::test::isCompleted(incomplete_file));
949 }
950
951 process.add<std::string>("pass_name", "test");
952 std::string hist_file_path = "test_incompletein_hists.root";
953 process.add("histogram_file", hist_file_path);
954 process.add<std::vector<framework::config::Parameters>>(
955 "sequence", {analyzer_parameters});
956
957 SECTION("an unfinished run is refused") {
958 process.add("input_files", std::vector<std::string>{incomplete_file});
959 CHECK_THROWS_AS(framework::test::runProcess(process),
961 }
962
963 SECTION("an unfinished run is refused even alongside good files") {
964 process.add("input_files",
965 std::vector<std::string>{complete_file, incomplete_file});
966 CHECK_THROWS_AS(framework::test::runProcess(process),
968 }
969
970 SECTION("experts can ask for it anyway") {
971 process.add("input_files", std::vector<std::string>{incomplete_file});
972 process.add("allow_incomplete_input_files", true);
973 CHECK(framework::test::runProcess(process));
974 CHECK_THAT(hist_file_path, framework::test::IsGoodhistogramFile(1 + 2 + 3));
975 }
976
977 SECTION("a complete run is let through") {
978 process.add("input_files", std::vector<std::string>{complete_file});
979 CHECK(framework::test::runProcess(process));
980 CHECK_THAT(hist_file_path, framework::test::IsGoodhistogramFile(1 + 2 + 3));
981 }
982
983 CHECK(framework::test::removeFile(hist_file_path));
984 CHECK(framework::test::removeFile(incomplete_file));
985 CHECK(framework::test::removeFile(complete_file));
986} // 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:42
Class which represents the process under execution.
Definition Process.h:37
Base class for a module which produces a data product.
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:29
void add(const std::string &name, const T &value)
Add a parameter to the parameter list.
Definition Parameters.h:42
const T & get(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:78
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:67
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:302
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:246