diff --git a/docs/howto/other-howtos.md b/docs/howto/other-howtos.md index 5da2862a8..bc08cc8d1 100644 --- a/docs/howto/other-howtos.md +++ b/docs/howto/other-howtos.md @@ -71,13 +71,15 @@ The following configuration options are used most commonly: | nthreads | int | Size of thread team (Defaults to the number of cores on your machine) | | plugins | string | Comma-separated list of plugin filenames. JANA will look for these on the `$JANA_PLUGIN_PATH` | | plugins_to_ignore | string | This removes plugins which had been specified in `plugins`. | -| event_source_type | string | Manually override JANA's decision about which JEventSource to use | +| event_source_type | string | Manually specify which JEventSource to use | | jana:nevents | int | Limit the number of events each source may emit | | jana:nskip | int | Skip processing the first n events from each event source | -| jana:extended_report | bool | The amount of status information to show while running | | jana:status_fname | string | Named pipe for retrieving status information remotely | | jana:loglevel | string | Set the log level (trace,debug,info,warn,error,fatal,off) for loggers internal to JANA | | jana:global_loglevel | string | Set the default log level (trace,debug,info,warn,error,fatal,off) for all loggers | +| jana:show_ticker | bool | Controls whether the status ticker is shown | +| jana:ticker_interval | int | Controls how often the status ticker updates (in ms) | +| jana:extended_report | bool | Controls whether to show extra details in the status ticker and final report | JANA automatically provides each component with its own logger. You can control the logging verbosity of individual components just like any other parameter. For instance, if your component prefixes its parameters with `BCAL:tracking`, diff --git a/src/libraries/JANA/CMakeLists.txt b/src/libraries/JANA/CMakeLists.txt index 627e54a86..396b035f3 100644 --- a/src/libraries/JANA/CMakeLists.txt +++ b/src/libraries/JANA/CMakeLists.txt @@ -4,12 +4,14 @@ set(JANA2_SOURCES JApplication.cc + JEvent.cc JEventSource.cc JFactory.cc JFactorySet.cc JMultifactory.cc JService.cc JVersion.cc + JEvent.cc Engine/JArrowProcessingController.cc Engine/JScheduler.cc diff --git a/src/libraries/JANA/Engine/JScheduler.cc b/src/libraries/JANA/Engine/JScheduler.cc index bbae230c9..0eebcb0ce 100644 --- a/src/libraries/JANA/Engine/JScheduler.cc +++ b/src/libraries/JANA/Engine/JScheduler.cc @@ -284,6 +284,15 @@ void JScheduler::finish_topology() { std::lock_guard lock(m_mutex); // This finalizes all arrows. Once this happens, we cannot restart the topology. // assert(m_topology_state.current_topology_status == TopologyStatus::Inactive); + + // We ensure that JFactory::EndRun() and JFactory::Finish() are called _before_ + // JApplication::Run() exits. This leaves the topology in an unrunnable state, + // but it won't be resumable anyway once it reaches Status::Finished. + for (JEventPool* pool : m_topology->pools) { + pool->finalize(); + } + + // Next we finalize all remaining components (particularly JEventProcessors) for (ArrowState& as : m_topology_state.arrow_states) { if (as.status != ArrowStatus::Finalized) { diff --git a/src/libraries/JANA/JApplication.cc b/src/libraries/JANA/JApplication.cc index 98d4cc453..cf2938658 100644 --- a/src/libraries/JANA/JApplication.cc +++ b/src/libraries/JANA/JApplication.cc @@ -149,6 +149,7 @@ void JApplication::Initialize() { m_desired_nthreads = JCpuInfo::GetNumCpus(); } + m_params->SetDefaultParameter("jana:show_ticker", m_ticker_on, "Controls whether the ticker is visible"); m_params->SetDefaultParameter("jana:ticker_interval", m_ticker_interval_ms, "Controls the ticker interval (in ms)"); m_params->SetDefaultParameter("jana:extended_report", m_extended_report, "Controls whether the ticker shows simple vs detailed performance metrics"); diff --git a/src/libraries/JANA/JEvent.cc b/src/libraries/JANA/JEvent.cc new file mode 100644 index 000000000..cba870184 --- /dev/null +++ b/src/libraries/JANA/JEvent.cc @@ -0,0 +1,125 @@ + +#include +#include + + +JEvent::JEvent() : mInspector(this){ + mFactorySet = new JFactorySet(); +} + +JEvent::JEvent(JApplication* app) : mInspector(this) { + // Furnish the JEvent with the parameter values and factory generators provided to the JApplication + app->Initialize(); + app->GetService()->configure_event(*this); +} + +JEvent::~JEvent() { + if (mFactorySet != nullptr) { + // Prevent memory leaks of factory contents + mFactorySet->Clear(); + // We mustn't call EndRun() or Finish() here because that would give us an excepting destructor + } + delete mFactorySet; +} + +void JEvent::SetFactorySet(JFactorySet* factorySet) { + delete mFactorySet; + mFactorySet = factorySet; +#if JANA2_HAVE_PODIO + // Maintain the index of PODIO factories + for (JFactory* factory : mFactorySet->GetAllFactories()) { + if (dynamic_cast(factory) != nullptr) { + auto tag = factory->GetTag(); + auto it = mPodioFactories.find(tag); + if (it != mPodioFactories.end()) { + throw JException("SetFactorySet failed because PODIO factory tag '%s' is not unique", tag.c_str()); + } + mPodioFactories[tag] = factory; + } + } +#endif +} + + +/// GetFactory() should be used with extreme care because it subverts the JEvent abstraction. +/// Most historical uses of GetFactory are far better served by JMultifactory +JFactory* JEvent::GetFactory(const std::string& object_name, const std::string& tag) const { + return mFactorySet->GetFactory(object_name, tag); +} + +/// GetAllFactories() should be used with extreme care because it subverts the JEvent abstraction. +/// Most historical uses of GetFactory are far better served by JMultifactory +std::vector JEvent::GetAllFactories() const { + return mFactorySet->GetAllFactories(); +} + +bool JEvent::HasParent(JEventLevel level) const { + for (const auto& pair : mParents) { + if (pair.first == level) return true; + } + return false; +} + +const JEvent& JEvent::GetParent(JEventLevel level) const { + for (const auto& pair : mParents) { + if (pair.first == level) return *(*(pair.second)); + } + throw JException("Unable to find parent at level %s", + toString(level).c_str()); +} + +void JEvent::SetParent(std::shared_ptr* parent) { + JEventLevel level = parent->get()->GetLevel(); + for (const auto& pair : mParents) { + if (pair.first == level) throw JException("Event already has a parent at level %s", + toString(parent->get()->GetLevel()).c_str()); + } + mParents.push_back({level, parent}); + parent->get()->mReferenceCount.fetch_add(1); +} + +std::shared_ptr* JEvent::ReleaseParent(JEventLevel level) { + if (mParents.size() == 0) { + throw JException("ReleaseParent failed: child has no parents!"); + } + auto pair = mParents.back(); + if (pair.first != level) { + throw JException("JEvent::ReleaseParent called out of level order: Caller expected %s, but parent was actually %s", + toString(level).c_str(), toString(pair.first).c_str()); + } + mParents.pop_back(); + auto remaining_refs = pair.second->get()->mReferenceCount.fetch_sub(1); + if (remaining_refs < 1) { // Remember, this was fetched _before_ the last subtraction + throw JException("Parent refcount has gone negative!"); + } + if (remaining_refs == 1) { + return pair.second; + // Parent is no longer shared. Transfer back to arrow + } + else { + return nullptr; // Parent is still shared by other children + } +} + +void JEvent::Release() { + auto remaining_refs = mReferenceCount.fetch_sub(1); + if (remaining_refs < 0) { + throw JException("JEvent's own refcount has gone negative!"); + } +} + +void JEvent::Clear() { + if (mEventSource != nullptr) { + mEventSource->DoFinishEvent(*this); + } + mFactorySet->Clear(); + mInspector.Reset(); + mCallGraph.Reset(); + mReferenceCount = 1; +} + +void JEvent::Finish() { + mFactorySet->Finish(); +} + + diff --git a/src/libraries/JANA/JEvent.h b/src/libraries/JANA/JEvent.h index ad87250e8..1e40ec132 100644 --- a/src/libraries/JANA/JEvent.h +++ b/src/libraries/JANA/JEvent.h @@ -9,7 +9,6 @@ #include #include #include - #include #include @@ -22,9 +21,7 @@ #include #include #include -#include #include -#include #if JANA2_HAVE_PODIO #include @@ -37,243 +34,106 @@ class JApplication; class JEventSource; -class JEvent : public std::enable_shared_from_this -{ - public: - - explicit JEvent(JApplication* aApplication=nullptr) : mInspector(&(*this)) { - mApplication = aApplication; - mFactorySet = new JFactorySet(); - } - virtual ~JEvent() { - if (mFactorySet != nullptr) mFactorySet->Release(); - delete mFactorySet; - } - - void SetFactorySet(JFactorySet* aFactorySet) { - delete mFactorySet; - mFactorySet = aFactorySet; -#if JANA2_HAVE_PODIO - // Maintain the index of PODIO factories - for (JFactory* factory : mFactorySet->GetAllFactories()) { - if (dynamic_cast(factory) != nullptr) { - auto tag = factory->GetTag(); - auto it = mPodioFactories.find(tag); - if (it != mPodioFactories.end()) { - throw JException("SetFactorySet failed because PODIO factory tag '%s' is not unique", tag.c_str()); - } - mPodioFactories[tag] = factory; - } - } -#endif - } - - JFactorySet* GetFactorySet() const { return mFactorySet; } - - JFactory* GetFactory(const std::string& object_name, const std::string& tag) const; - std::vector GetAllFactories() const; - template JFactoryT* GetFactory(const std::string& tag = "", bool throw_on_missing=false) const; - template std::vector*> GetFactoryAll(bool throw_on_missing = false) const; - - //OBJECTS - // C style getters - template JFactoryT* Get(const T** item, const std::string& tag="") const; - template JFactoryT* Get(std::vector &vec, const std::string& tag = "", bool strict=true) const; - template void GetAll(std::vector &vec) const; +class JEvent : public std::enable_shared_from_this { - // C++ style getters - template const T* GetSingle(const std::string& tag = "") const; - template const T* GetSingleStrict(const std::string& tag = "") const; - template std::vector Get(const std::string& tag = "", bool strict=true) const; - template typename JFactoryT::PairType GetIterators(const std::string& aTag = "") const; - template std::vector GetAll() const; - template std::map,std::vector> GetAllChildren() const; +private: + JApplication* mApplication = nullptr; + int32_t mRunNumber = 0; + uint64_t mEventNumber = 0; + mutable JFactorySet* mFactorySet = nullptr; + mutable JCallGraphRecorder mCallGraph; + mutable JInspector mInspector; + bool mUseDefaultTags = false; + std::map mDefaultTags; + JEventSource* mEventSource = nullptr; + bool mIsBarrierEvent = false; - // JANA1 compatibility getters - template JFactoryT* GetSingle(const T* &t, const char *tag="", bool exception_if_not_one=true) const; + // Hierarchical event memory management + std::vector*>> mParents; + std::atomic_int mReferenceCount {1}; + int64_t mEventIndex = -1; - // Insert - template JFactoryT* Insert(T* item, const std::string& aTag = "") const; - template JFactoryT* Insert(const std::vector& items, const std::string& tag = "") const; - - // PODIO #if JANA2_HAVE_PODIO - std::vector GetAllCollectionNames() const; - const podio::CollectionBase* GetCollectionBase(std::string name, bool throw_on_missing=true) const; - template const typename JFactoryPodioT::CollectionT* GetCollection(std::string name, bool throw_on_missing=true) const; - template JFactoryPodioT* InsertCollection(typename JFactoryPodioT::CollectionT&& collection, std::string name); - template JFactoryPodioT* InsertCollectionAlreadyInFrame(const podio::CollectionBase* collection, std::string name); + std::map mPodioFactories; #endif - //SETTERS - void SetRunNumber(int32_t aRunNumber){mRunNumber = aRunNumber;} - void SetEventNumber(uint64_t aEventNumber){mEventNumber = aEventNumber;} - void SetJApplication(JApplication* app){mApplication = app;} - void SetJEventSource(JEventSource* aSource){mEventSource = aSource;} - void SetDefaultTags(std::map aDefaultTags){mDefaultTags=aDefaultTags; mUseDefaultTags = !mDefaultTags.empty();} - void SetSequential(bool isSequential) {mIsBarrierEvent = isSequential;} - - //GETTERS - int32_t GetRunNumber() const {return mRunNumber;} - uint64_t GetEventNumber() const {return mEventNumber;} - JApplication* GetJApplication() const {return mApplication;} - JEventSource* GetJEventSource() const {return mEventSource; } - JCallGraphRecorder* GetJCallGraphRecorder() const {return &mCallGraph;} - JInspector* GetJInspector() const {return &mInspector;} - void Inspect() const { mInspector.Loop();} // TODO: Force this not to be inlined AND used so it is defined in libJANA.a - bool GetSequential() const {return mIsBarrierEvent;} - friend class JEventPool; - - - // Hierarchical - JEventLevel GetLevel() const { return mFactorySet->GetLevel(); } - void SetLevel(JEventLevel level) { mFactorySet->SetLevel(level); } - void SetEventIndex(int event_index) { mEventIndex = event_index; } - int64_t GetEventIndex() const { return mEventIndex; } - - bool HasParent(JEventLevel level) const { - for (const auto& pair : mParents) { - if (pair.first == level) return true; - } - return false; - } - - const JEvent& GetParent(JEventLevel level) const { - for (const auto& pair : mParents) { - if (pair.first == level) return *(*(pair.second)); - } - throw JException("Unable to find parent at level %s", - toString(level).c_str()); - } - - void SetParent(std::shared_ptr* parent) { - JEventLevel level = parent->get()->GetLevel(); - for (const auto& pair : mParents) { - if (pair.first == level) throw JException("Event already has a parent at level %s", - toString(parent->get()->GetLevel()).c_str()); - } - mParents.push_back({level, parent}); - parent->get()->mReferenceCount.fetch_add(1); - } - - std::shared_ptr* ReleaseParent(JEventLevel level) { - if (mParents.size() == 0) { - throw JException("ReleaseParent failed: child has no parents!"); - } - auto pair = mParents.back(); - if (pair.first != level) { - throw JException("JEvent::ReleaseParent called out of level order: Caller expected %s, but parent was actually %s", - toString(level).c_str(), toString(pair.first).c_str()); - } - mParents.pop_back(); - auto remaining_refs = pair.second->get()->mReferenceCount.fetch_sub(1); - if (remaining_refs < 1) { // Remember, this was fetched _before_ the last subtraction - throw JException("Parent refcount has gone negative!"); - } - if (remaining_refs == 1) { - return pair.second; - // Parent is no longer shared. Transfer back to arrow - } - else { - return nullptr; // Parent is still shared by other children - } - } - - void Release() { - auto remaining_refs = mReferenceCount.fetch_sub(1); - if (remaining_refs < 0) { - throw JException("JEvent's own refcount has gone negative!"); - } - } - - void Reset() { - mReferenceCount = 1; - } - - - private: - JApplication* mApplication = nullptr; - int32_t mRunNumber = 0; - uint64_t mEventNumber = 0; - mutable JFactorySet* mFactorySet = nullptr; - mutable JCallGraphRecorder mCallGraph; - mutable JInspector mInspector; - bool mUseDefaultTags = false; - std::map mDefaultTags; - JEventSource* mEventSource = nullptr; - bool mIsBarrierEvent = false; - - // Hierarchical stuff - std::vector*>> mParents; - std::atomic_int mReferenceCount {1}; - int64_t mEventIndex = -1; - - +public: + JEvent(); + explicit JEvent(JApplication* app); + virtual ~JEvent(); + + void SetFactorySet(JFactorySet* aFactorySet); + void SetRunNumber(int32_t aRunNumber){mRunNumber = aRunNumber;} + void SetEventNumber(uint64_t aEventNumber){mEventNumber = aEventNumber;} + void SetJApplication(JApplication* app){mApplication = app;} + void SetJEventSource(JEventSource* aSource){mEventSource = aSource;} + void SetDefaultTags(std::map aDefaultTags){mDefaultTags=aDefaultTags; mUseDefaultTags = !mDefaultTags.empty();} + void SetSequential(bool isSequential) {mIsBarrierEvent = isSequential;} + + JFactorySet* GetFactorySet() const { return mFactorySet; } + int32_t GetRunNumber() const {return mRunNumber;} + uint64_t GetEventNumber() const {return mEventNumber;} + JApplication* GetJApplication() const {return mApplication;} + JEventSource* GetJEventSource() const {return mEventSource; } + JCallGraphRecorder* GetJCallGraphRecorder() const {return &mCallGraph;} + JInspector* GetJInspector() const {return &mInspector;} + void Inspect() const { mInspector.Loop();} + bool GetSequential() const {return mIsBarrierEvent;} + + // Hierarchical + JEventLevel GetLevel() const { return mFactorySet->GetLevel(); } + void SetLevel(JEventLevel level) { mFactorySet->SetLevel(level); } + void SetEventIndex(int event_index) { mEventIndex = event_index; } + int64_t GetEventIndex() const { return mEventIndex; } + + bool HasParent(JEventLevel level) const; + const JEvent& GetParent(JEventLevel level) const; + void SetParent(std::shared_ptr* parent); + std::shared_ptr* ReleaseParent(JEventLevel level); + void Release(); + + // Lifecycle + void Clear(); + void Finish(); + + JFactory* GetFactory(const std::string& object_name, const std::string& tag) const; + std::vector GetAllFactories() const; + + + template JFactoryT* GetFactory(const std::string& tag = "", bool throw_on_missing=false) const; + template std::vector*> GetFactoryAll(bool throw_on_missing = false) const; + + // C style getters + template JFactoryT* GetSingle(const T* &t, const char *tag="", bool exception_if_not_one=true) const; + template JFactoryT* Get(const T** item, const std::string& tag="") const; + template JFactoryT* Get(std::vector &vec, const std::string& tag = "", bool strict=true) const; + template void GetAll(std::vector &vec) const; + + // C++ style getters + template const T* GetSingle(const std::string& tag = "") const; + template const T* GetSingleStrict(const std::string& tag = "") const; + template std::vector Get(const std::string& tag = "", bool strict=true) const; + template typename JFactoryT::PairType GetIterators(const std::string& aTag = "") const; + template std::vector GetAll() const; + template std::map,std::vector> GetAllChildren() const; + + // Insert + template JFactoryT* Insert(T* item, const std::string& aTag = "") const; + template JFactoryT* Insert(const std::vector& items, const std::string& tag = "") const; + + // PODIO #if JANA2_HAVE_PODIO - std::map mPodioFactories; + std::vector GetAllCollectionNames() const; + const podio::CollectionBase* GetCollectionBase(std::string name, bool throw_on_missing=true) const; + template const typename JFactoryPodioT::CollectionT* GetCollection(std::string name, bool throw_on_missing=true) const; + template JFactoryPodioT* InsertCollection(typename JFactoryPodioT::CollectionT&& collection, std::string name); + template JFactoryPodioT* InsertCollectionAlreadyInFrame(const podio::CollectionBase* collection, std::string name); #endif -}; - -/// Insert() allows an EventSource to insert items directly into the JEvent, -/// removing the need for user-extended JEvents and/or JEventSource::GetObjects(...) -/// Repeated calls to Insert() will append to the previous data rather than overwrite it, -/// which saves the user from having to allocate a throwaway vector and requires less error handling. -template -inline JFactoryT* JEvent::Insert(T* item, const std::string& tag) const { - std::string resolved_tag = tag; - if (mUseDefaultTags && tag.empty()) { - auto defaultTag = mDefaultTags.find(JTypeInfo::demangle()); - if (defaultTag != mDefaultTags.end()) resolved_tag = defaultTag->second; - } - auto factory = mFactorySet->GetFactory(resolved_tag); - if (factory == nullptr) { - factory = new JFactoryT; - factory->SetTag(tag); - factory->SetLevel(mFactorySet->GetLevel()); - mFactorySet->Add(factory); - } - factory->Insert(item); - factory->SetInsertOrigin( mCallGraph.GetInsertDataOrigin() ); // (see note at top of JCallGraphRecorder.h) - return factory; -} - -template -inline JFactoryT* JEvent::Insert(const std::vector& items, const std::string& tag) const { - - std::string resolved_tag = tag; - if (mUseDefaultTags && tag.empty()) { - auto defaultTag = mDefaultTags.find(JTypeInfo::demangle()); - if (defaultTag != mDefaultTags.end()) resolved_tag = defaultTag->second; - } - auto factory = mFactorySet->GetFactory(resolved_tag); - if (factory == nullptr) { - factory = new JFactoryT; - factory->SetTag(tag); - factory->SetLevel(mFactorySet->GetLevel()); - mFactorySet->Add(factory); - } - for (T* item : items) { - factory->Insert(item); - } - factory->SetStatus(JFactory::Status::Inserted); // for when items is empty - factory->SetCreationStatus(JFactory::CreationStatus::Inserted); // for when items is empty - factory->SetInsertOrigin( mCallGraph.GetInsertDataOrigin() ); // (see note at top of JCallGraphRecorder.h) - return factory; -} -/// GetFactory() should be used with extreme care because it subverts the JEvent abstraction. -/// Most historical uses of GetFactory are far better served by JMultifactory -inline JFactory* JEvent::GetFactory(const std::string& object_name, const std::string& tag) const { - return mFactorySet->GetFactory(object_name, tag); -} +}; -/// GetAllFactories() should be used with extreme care because it subverts the JEvent abstraction. -/// Most historical uses of GetFactory are far better served by JMultifactory -inline std::vector JEvent::GetAllFactories() const { - return mFactorySet->GetAllFactories(); -} /// GetFactory() should be used with extreme care because it subverts the JEvent abstraction. /// Most historical uses of GetFactory are far better served by JMultifactory. @@ -297,9 +157,52 @@ inline JFactoryT* JEvent::GetFactory(const std::string& tag, bool throw_on_mi } +/// GetFactoryAll returns all JFactoryT's for type T (each corresponds to a different tag). +/// This is useful when there are many different tags, or the tags are unknown, and the user +/// wishes to examine them all together. +template +inline std::vector*> JEvent::GetFactoryAll(bool throw_on_missing) const { + auto factories = mFactorySet->GetAllFactories(); + if (factories.size() == 0) { + if (throw_on_missing) { + JException ex("Could not find any JFactoryT<" + JTypeInfo::demangle() + "> (from any tag)"); + ex.show_stacktrace = false; + throw ex; + } + }; + return factories; +} + /// C-style getters +template +JFactoryT* JEvent::GetSingle(const T* &t, const char *tag, bool exception_if_not_one) const +{ + /// This is a convenience method that can be used to get a pointer to the single + /// object of type T from the specified factory. It simply calls the Get(vector<...>) method + /// and copies the first pointer into "t" (or NULL if something other than 1 object is returned). + /// + /// This is intended to address the common situation in which there is an interest + /// in the event if and only if there is exactly 1 object of type T. If the event + /// has no objects of that type or more than 1 object of that type (for the specified + /// factory) then an exception of type "unsigned long" is thrown with the value + /// being the number of objects of type T. You can supress the exception by setting + /// exception_if_not_one to false. In that case, you will have to check if t==NULL to + /// know if the call succeeded. + + std::vector v; + auto fac = GetFactory(tag, true); // throw exception if factory not found + JCallGraphEntryMaker cg_entry(mCallGraph, fac); // times execution until this goes out of scope + Get(v, tag); + if(v.size()!=1){ + t = NULL; + if(exception_if_not_one) throw v.size(); + } + t = v[0]; + return fac; +} + /// Get conveniently returns one item from inside the JFactory. This should be used when the data in question /// is optional and the caller wants to examine the result and decide how to proceed. The caller should embed this /// inside an if-block. Get updates the `destination` out parameter and returns a pointer to the enclosing JFactory. @@ -337,6 +240,20 @@ JFactoryT* JEvent::Get(std::vector& destination, const std::string& } +/// GetAll returns all JObjects of (child) type T, regardless of tag. +template +void JEvent::GetAll(std::vector& destination) const { + auto factories = GetFactoryAll(true); + for (auto factory : factories) { + auto iterators = factory->CreateAndGetData(this->shared_from_this()); + for (auto it = iterators.first; it != iterators.second; it++) { + destination.push_back(*it); + } + } +} + + + /// C++ style getters /// GetSingle conveniently returns one item from inside the JFactory. This should be used when the data in question @@ -356,6 +273,8 @@ template const T* JEvent::GetSingle(const std::string& tag) const { return *iterators.first; } + + /// GetSingleStrict conveniently returns one item from inside the JFactory. This should be used when the data in /// question is mandatory, and its absence indicates an error which should stop execution. The caller does not need /// to embed this in an if- or try-catch block; it can be a one-liner. @@ -394,32 +313,12 @@ std::vector JEvent::Get(const std::string& tag, bool strict) const { return vec; // Assumes RVO } -/// GetFactoryAll returns all JFactoryT's for type T (each corresponds to a different tag). -/// This is useful when there are many different tags, or the tags are unknown, and the user -/// wishes to examine them all together. -template -inline std::vector*> JEvent::GetFactoryAll(bool throw_on_missing) const { - auto factories = mFactorySet->GetAllFactories(); - if (factories.size() == 0) { - if (throw_on_missing) { - JException ex("Could not find any JFactoryT<" + JTypeInfo::demangle() + "> (from any tag)"); - ex.show_stacktrace = false; - throw ex; - } - }; - return factories; -} - -/// GetAll returns all JObjects of (child) type T, regardless of tag. template -void JEvent::GetAll(std::vector& destination) const { - auto factories = GetFactoryAll(true); - for (auto factory : factories) { - auto iterators = factory->CreateAndGetData(this->shared_from_this()); - for (auto it = iterators.first; it != iterators.second; it++) { - destination.push_back(*it); - } - } +typename JFactoryT::PairType JEvent::GetIterators(const std::string& tag) const { + auto factory = GetFactory(tag, true); + JCallGraphEntryMaker cg_entry(mCallGraph, factory); // times execution until this goes out of scope + auto iters = factory->CreateAndGetData(this->shared_from_this()); + return iters; } /// GetAll returns all JObjects of (child) type T, regardless of tag. @@ -438,7 +337,6 @@ std::vector JEvent::GetAll() const { return vec; // Assumes RVO } - // GetAllChildren will furnish a map { (type_name,tag_name) : [BaseClass*] } containing all JFactoryT data where // T inherits from BaseClass. Note that this _won't_ compute any results (unlike GetAll) because this is meant for // things like visualizing and persisting DSTs. @@ -458,42 +356,57 @@ std::map, std::vector> JEvent::GetAllChi } -template -typename JFactoryT::PairType JEvent::GetIterators(const std::string& tag) const { - auto factory = GetFactory(tag, true); - JCallGraphEntryMaker cg_entry(mCallGraph, factory); // times execution until this goes out of scope - auto iters = factory->CreateAndGetData(this->shared_from_this()); - return iters; -} +/// Insert() allows an EventSource to insert items directly into the JEvent, +/// removing the need for user-extended JEvents and/or JEventSource::GetObjects(...) +/// Repeated calls to Insert() will append to the previous data rather than overwrite it, +/// which saves the user from having to allocate a throwaway vector and requires less error handling. +template +inline JFactoryT* JEvent::Insert(T* item, const std::string& tag) const { -template -JFactoryT* JEvent::GetSingle(const T* &t, const char *tag, bool exception_if_not_one) const -{ - /// This is a convenience method that can be used to get a pointer to the single - /// object of type T from the specified factory. It simply calls the Get(vector<...>) method - /// and copies the first pointer into "t" (or NULL if something other than 1 object is returned). - /// - /// This is intended to address the common situation in which there is an interest - /// in the event if and only if there is exactly 1 object of type T. If the event - /// has no objects of that type or more than 1 object of that type (for the specified - /// factory) then an exception of type "unsigned long" is thrown with the value - /// being the number of objects of type T. You can supress the exception by setting - /// exception_if_not_one to false. In that case, you will have to check if t==NULL to - /// know if the call succeeded. + std::string resolved_tag = tag; + if (mUseDefaultTags && tag.empty()) { + auto defaultTag = mDefaultTags.find(JTypeInfo::demangle()); + if (defaultTag != mDefaultTags.end()) resolved_tag = defaultTag->second; + } + auto factory = mFactorySet->GetFactory(resolved_tag); + if (factory == nullptr) { + factory = new JFactoryT; + factory->SetTag(tag); + factory->SetLevel(mFactorySet->GetLevel()); + mFactorySet->Add(factory); + } + factory->Insert(item); + factory->SetInsertOrigin( mCallGraph.GetInsertDataOrigin() ); // (see note at top of JCallGraphRecorder.h) + return factory; +} - std::vector v; - auto fac = GetFactory(tag, true); // throw exception if factory not found - JCallGraphEntryMaker cg_entry(mCallGraph, fac); // times execution until this goes out of scope - Get(v, tag); - if(v.size()!=1){ - t = NULL; - if(exception_if_not_one) throw v.size(); +template +inline JFactoryT* JEvent::Insert(const std::vector& items, const std::string& tag) const { + + std::string resolved_tag = tag; + if (mUseDefaultTags && tag.empty()) { + auto defaultTag = mDefaultTags.find(JTypeInfo::demangle()); + if (defaultTag != mDefaultTags.end()) resolved_tag = defaultTag->second; } - t = v[0]; - return fac; + auto factory = mFactorySet->GetFactory(resolved_tag); + if (factory == nullptr) { + factory = new JFactoryT; + factory->SetTag(tag); + factory->SetLevel(mFactorySet->GetLevel()); + mFactorySet->Add(factory); + } + for (T* item : items) { + factory->Insert(item); + } + factory->SetStatus(JFactory::Status::Inserted); // for when items is empty + factory->SetCreationStatus(JFactory::CreationStatus::Inserted); // for when items is empty + factory->SetInsertOrigin( mCallGraph.GetInsertDataOrigin() ); // (see note at top of JCallGraphRecorder.h) + return factory; } + + #if JANA2_HAVE_PODIO inline std::vector JEvent::GetAllCollectionNames() const { @@ -522,9 +435,9 @@ inline const podio::CollectionBase* JEvent::GetCollectionBase(std::string name, JCallGraphEntryMaker cg_entry(mCallGraph, it->second); // times execution until this goes out of scope it->second->Create(this->shared_from_this()); return factory->GetCollection(); - // TODO: Might be cheaper/simpler to obtain factory from mPodioFactories instead of mFactorySet } + template const typename JFactoryPodioT::CollectionT* JEvent::GetCollection(std::string name, bool throw_on_missing) const { JFactoryT* factory = GetFactory(name, throw_on_missing); diff --git a/src/libraries/JANA/JEventSource.cc b/src/libraries/JANA/JEventSource.cc index 62c3377e0..af99d9fc4 100644 --- a/src/libraries/JANA/JEventSource.cc +++ b/src/libraries/JANA/JEventSource.cc @@ -249,7 +249,7 @@ JEventSource::Result JEventSource::DoNextCompatibility(std::shared_ptr e } -void JEventSource::DoFinish(JEvent& event) { +void JEventSource::DoFinishEvent(JEvent& event) { m_events_finished.fetch_add(1); if (m_enable_finish_event) { diff --git a/src/libraries/JANA/JEventSource.h b/src/libraries/JANA/JEventSource.h index 1b5111f86..c3cc96048 100644 --- a/src/libraries/JANA/JEventSource.h +++ b/src/libraries/JANA/JEventSource.h @@ -188,7 +188,7 @@ class JEventSource : public jana::components::JComponent, Result DoNextCompatibility(std::shared_ptr event); - void DoFinish(JEvent& event); + void DoFinishEvent(JEvent& event); void Summarize(JComponentSummary& summary) const override; diff --git a/src/libraries/JANA/JFactory.cc b/src/libraries/JANA/JFactory.cc index 757c01ce6..29ec12077 100644 --- a/src/libraries/JANA/JFactory.cc +++ b/src/libraries/JANA/JFactory.cc @@ -45,15 +45,10 @@ void JFactory::Create(const std::shared_ptr& event) { if (mStatus == Status::Unprocessed) { auto run_number = event->GetRunNumber(); - if (mPreviousRunNumber == -1) { - // This is the very first run - CallWithJExceptionWrapper("JFactory::ChangeRun", [&](){ ChangeRun(event); }); - CallWithJExceptionWrapper("JFactory::BeginRun", [&](){ BeginRun(event); }); - mPreviousRunNumber = run_number; - } - else if (mPreviousRunNumber != run_number) { - // This is a later run, and it has changed - CallWithJExceptionWrapper("JFactory::EndRun", [&](){ EndRun(); }); + if (mPreviousRunNumber != run_number) { + if (mPreviousRunNumber != -1) { + CallWithJExceptionWrapper("JFactory::EndRun", [&](){ EndRun(); }); + } CallWithJExceptionWrapper("JFactory::ChangeRun", [&](){ ChangeRun(event); }); CallWithJExceptionWrapper("JFactory::BeginRun", [&](){ BeginRun(event); }); mPreviousRunNumber = run_number; @@ -78,6 +73,16 @@ void JFactory::DoInit() { mStatus = Status::Unprocessed; } +void JFactory::DoFinish() { + if (mStatus == Status::Unprocessed || mStatus == Status::Processed) { + if (mPreviousRunNumber != -1) { + CallWithJExceptionWrapper("JFactory::EndRun", [&](){ EndRun(); }); + } + CallWithJExceptionWrapper("JFactory::Finish", [&](){ Finish(); }); + mStatus = Status::Finished; + } +} + void JFactory::Summarize(JComponentSummary& summary) const { auto fs = new JComponentSummary::Component( diff --git a/src/libraries/JANA/JFactory.h b/src/libraries/JANA/JFactory.h index 823c324e7..da3066fea 100644 --- a/src/libraries/JANA/JFactory.h +++ b/src/libraries/JANA/JFactory.h @@ -25,7 +25,7 @@ class JApplication; class JFactory : public jana::components::JComponent { public: - enum class Status {Uninitialized, Unprocessed, Processed, Inserted}; + enum class Status {Uninitialized, Unprocessed, Processed, Inserted, Finished}; enum class CreationStatus { NotCreatedYet, Created, Inserted, InsertedViaGetObjects, NeverCreated }; enum JFactory_Flags_t { @@ -171,6 +171,7 @@ class JFactory : public jana::components::JComponent { /// type of object contained. In order to access these objects when all you have is a JFactory*, use JFactory::GetAs(). virtual void Create(const std::shared_ptr& event); void DoInit(); + void DoFinish(); void Summarize(JComponentSummary& summary) const override; diff --git a/src/libraries/JANA/JFactorySet.cc b/src/libraries/JANA/JFactorySet.cc index 3b29258dc..05d83a4df 100644 --- a/src/libraries/JANA/JFactorySet.cc +++ b/src/libraries/JANA/JFactorySet.cc @@ -165,12 +165,27 @@ void JFactorySet::Print() const } } -/// Release() loops over all contained factories, clearing their data -void JFactorySet::Release() { +//--------------------------------- +// Clear +//--------------------------------- +void JFactorySet::Clear() { for (const auto& sFactoryPair : mFactories) { auto sFactory = sFactoryPair.second; sFactory->ClearData(); + // This automatically clears multifactories because their data is stored in helper factories! + } +} + +//--------------------------------- +// Finish +//--------------------------------- +void JFactorySet::Finish() { + for (auto& p : mFactories) { + p.second->DoFinish(); + } + for (auto& multifac : mMultifactories) { + multifac->DoFinish(); } } diff --git a/src/libraries/JANA/JFactorySet.h b/src/libraries/JANA/JFactorySet.h index 25ebc50c0..706883169 100644 --- a/src/libraries/JANA/JFactorySet.h +++ b/src/libraries/JANA/JFactorySet.h @@ -26,8 +26,9 @@ class JFactorySet { bool Add(JFactory* aFactory); bool Add(JMultifactory* multifactory); - void Print(void) const; - void Release(void); + void Print() const; + void Clear(); + void Finish(); JFactory* GetFactory(const std::string& object_name, const std::string& tag="") const; template JFactoryT* GetFactory(const std::string& tag = "") const; diff --git a/src/libraries/JANA/JMultifactory.cc b/src/libraries/JANA/JMultifactory.cc index a9783f9ad..f53c50fcb 100644 --- a/src/libraries/JANA/JMultifactory.cc +++ b/src/libraries/JANA/JMultifactory.cc @@ -46,14 +46,14 @@ void JMultifactory::Execute(const std::shared_ptr& event) { }); } -void JMultifactory::Release() { +void JMultifactory::DoFinish() { std::lock_guard lock(m_mutex); // Only call Finish() if we actually initialized // Only call Finish() once + if (m_status == Status::Initialized) { - CallWithJExceptionWrapper("JMultifactory::Finish", [&](){ - Finish(); - }); + CallWithJExceptionWrapper("JMultifactory::EndRun", [&](){ EndRun(); }); + CallWithJExceptionWrapper("JMultifactory::Finish", [&](){ Finish(); }); m_status = Status::Finalized; } } diff --git a/src/libraries/JANA/JMultifactory.h b/src/libraries/JANA/JMultifactory.h index d41dc1442..77f9832b6 100644 --- a/src/libraries/JANA/JMultifactory.h +++ b/src/libraries/JANA/JMultifactory.h @@ -118,12 +118,9 @@ class JMultifactory : public jana::components::JComponent, void DoInit(); - void Execute(const std::shared_ptr&); - // Should this be execute or create? Who is tracking that this is called at most once per event? - // Do we need something like JFactory::Status? Also, how do we ensure that CreationStatus is correct as well? + void DoFinish(); - void Release(); - // Release makes sure Finish() is called exactly once + void Execute(const std::shared_ptr&); JFactorySet* GetHelpers(); // This exposes the mHelpers JFactorySet, which contains a JFactoryT for each declared output of the multifactory. diff --git a/src/libraries/JANA/Services/JParameterManager.cc b/src/libraries/JANA/Services/JParameterManager.cc index d26e1d8df..7b8014af9 100644 --- a/src/libraries/JANA/Services/JParameterManager.cc +++ b/src/libraries/JANA/Services/JParameterManager.cc @@ -111,15 +111,9 @@ void JParameterManager::PrintParameters(int verbosity, int strictness) { return; } - bool warnings_present = false; bool strictness_violation = false; - // We don't need to show "Advanced" as a warning unless we are using full verbosity - if (verbosity == 3) { - warnings_present = true; - } - - // Check for warnings and unused parameters first + // Unused parameters first // The former might change the table columns and the latter might change the filter verbosity for (auto& pair : m_parameters) { const auto& key = pair.first; @@ -127,12 +121,10 @@ void JParameterManager::PrintParameters(int verbosity, int strictness) { if ((strictness > 0) && (!param->IsDefault()) && (!param->IsUsed())) { strictness_violation = true; - warnings_present = true; LOG_ERROR(m_logger) << "Parameter '" << key << "' appears to be unused. Possible typo?" << LOG_END; } if ((!param->IsDefault()) && (param->IsDeprecated())) { LOG_ERROR(m_logger) << "Parameter '" << key << "' has been deprecated and may no longer be supported in future releases." << LOG_END; - warnings_present = true; } } @@ -147,10 +139,10 @@ void JParameterManager::PrintParameters(int verbosity, int strictness) { for (auto& pair : m_parameters) { auto param = pair.second; - if (param->IsDeprecated() && (param->IsDefault())) continue; + if (param->IsDeprecated() && (param->IsDefault())) continue; // Always hide deprecated parameters that are NOT in use - if ((verbosity == 1) && (param->IsDefault())) continue; + if ((verbosity == 1) && (param->IsDefault())) continue; // At verbosity level 1, hide all default-valued parameters if ((verbosity == 2) && (param->IsDefault()) && (param->IsAdvanced())) continue; @@ -165,53 +157,48 @@ void JParameterManager::PrintParameters(int verbosity, int strictness) { return; } - // Print table - JTablePrinter table; - table.AddColumn("Name", JTablePrinter::Justify::Left, 20); - if (warnings_present) { - table.AddColumn("Warnings"); // IsDeprecated column - } - table.AddColumn("Value", JTablePrinter::Justify::Left, 25); - table.AddColumn("Default", JTablePrinter::Justify::Left, 25); - table.AddColumn("Description", JTablePrinter::Justify::Left, 50); - - std::ostringstream ss; + LOG_WARN(m_logger) << "Configuration Parameters" << LOG_END; for (JParameter* p: params_to_print) { - ss << std::endl; - ss << " - key: \"" << p->GetKey() << "\"" << std::endl; - ss << " value: \"" << p->GetValue() << "\"" << std::endl; - ss << " default: \"" << p->GetDefault() << "\"" << std::endl; + LOG_WARN(m_logger) << LOG_END; + LOG_WARN(m_logger) << " - key: " << p->GetKey() << LOG_END; + if (!p->IsDefault()) { + LOG_WARN(m_logger) << " value: " << p->GetValue() << LOG_END; + } + if (p->HasDefault()) { + LOG_WARN(m_logger) << " default: " << p->GetDefault() << LOG_END; + } if (!p->GetDescription().empty()) { - ss << " description: \""; std::istringstream iss(p->GetDescription()); std::string line; bool is_first_line = true; while (std::getline(iss, line)) { - if (!is_first_line) { - ss << std::endl << " " << line; + if (is_first_line) { + LOG_INFO(m_logger) << " description: " << line << LOG_END; } else { - ss << line; + LOG_INFO(m_logger) << " " << line << LOG_END; } is_first_line = false; } - ss << "\"" << std::endl; + } + if (p->IsConflicted()) { + LOG_WARN(m_logger) << " warning: Conflicting defaults" << LOG_END; } if (p->IsDeprecated()) { - ss << " warning: \"Deprecated\"" << std::endl; + LOG_WARN(m_logger) << " warning: Deprecated" << LOG_END; // If deprecated, it no longer matters whether it is advanced or not. If unused, won't show up here anyway. } - else if (!p->IsUsed()) { + if (!p->IsUsed()) { // Can't be both deprecated and unused, since JANA only finds out that it is deprecated by trying to use it // Can't be both advanced and unused, since JANA only finds out that it is advanced by trying to use it - ss << " warning: \"Unused\"" << std::endl; + LOG_WARN(m_logger) << " warning: Unused" << LOG_END; } - else if (p->IsAdvanced()) { - ss << " warning: \"Advanced\"" << std::endl; + if (p->IsAdvanced()) { + LOG_WARN(m_logger) << " warning: Advanced" << LOG_END; } } - LOG_WARN(m_logger) << "Configuration Parameters\n" << ss.str() << LOG_END; + LOG_WARN(m_logger) << LOG_END; // Now that we've printed the table, we can throw an exception if we are being super strict if (strictness_violation && strictness > 1) { diff --git a/src/libraries/JANA/Services/JParameterManager.h b/src/libraries/JANA/Services/JParameterManager.h index 6950d9357..7dfddcf77 100644 --- a/src/libraries/JANA/Services/JParameterManager.h +++ b/src/libraries/JANA/Services/JParameterManager.h @@ -34,6 +34,7 @@ class JParameter { // We want to differentiate these from the parameters that users are meant to control and understand. bool m_is_used = false; // If a parameter hasn't been used, it probably contains a typo, and we should warn the user. + bool m_is_conflicted = false; // Whether or not this parameter has been registered with inconsistent default values public: @@ -55,6 +56,7 @@ class JParameter { inline bool IsAdvanced() const { return m_is_advanced; } inline bool IsUsed() const { return m_is_used; } inline bool IsDeprecated() const { return m_is_deprecated; } + inline bool IsConflicted() const { return m_is_conflicted; } inline void SetKey(std::string key) { m_name = std::move(key); } inline void SetValue(std::string val) { m_value = std::move(val); } @@ -65,6 +67,7 @@ class JParameter { inline void SetIsAdvanced(bool isHidden) { m_is_advanced = isHidden; } inline void SetIsUsed(bool isUsed) { m_is_used = isUsed; } inline void SetIsDeprecated(bool isDeprecated) { m_is_deprecated = isDeprecated; } + inline void SetIsConflicted(bool isConflicted) { m_is_conflicted = isConflicted; } }; @@ -261,15 +264,20 @@ JParameter* JParameterManager::SetDefaultParameter(std::string name, T& val, std // However, we still want to warn the user if the same parameter was declared with different values. Parse(param->GetDefault(),t); if (!Equals(val, t)) { - LOG_WARN(m_logger) << "Parameter '" << name << "' has conflicting defaults: '" - << Stringify(val) << "' vs '" << param->GetDefault() << "'" - << LOG_END; - if (param->IsDefault()) { - // If we tried to set the same default parameter twice with different values, and there is no - // existing non-default value, we remember the _latest_ default value. This way, we return the - // default provided by the caller, instead of the default provided by the mysterious interloper. - param->SetValue(Stringify(val)); + if (!param->IsConflicted()) { + // Only show this warning once per parameter + LOG_WARN(m_logger) << "Parameter '" << name << "' has conflicting defaults: '" + << Stringify(val) << "' vs '" << param->GetDefault() << "'" + << LOG_END; + param->SetIsConflicted(true); } + // If we tried to set the same default parameter twice with different values, and there is no + // existing non-default value, we remember the _latest_ default value. This way, we return the + // default provided by the caller, instead of the default provided by the mysterious interloper. + param->SetDefault(Stringify(val)); + } + if (param->IsDefault()) { + param->SetValue(Stringify(val)); // Use _latest_ default value } } } diff --git a/src/libraries/JANA/Topology/JEventPool.h b/src/libraries/JANA/Topology/JEventPool.h index 8871b1251..4ac26491b 100644 --- a/src/libraries/JANA/Topology/JEventPool.h +++ b/src/libraries/JANA/Topology/JEventPool.h @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include @@ -65,14 +65,14 @@ class JEventPool { item->get()->SetLevel(m_level); // This needs to happen _after_ configure_event } - void release_item(std::shared_ptr* item) { - if (auto source = (*item)->GetJEventSource()) source->DoFinish(**item); - (*item)->mFactorySet->Release(); - (*item)->mInspector.Reset(); - (*item)->GetJCallGraphRecorder()->Reset(); - (*item)->Reset(); - } + void finalize() { + for (size_t pool_idx = 0; pool_idx < m_location_count; ++pool_idx) { + for (auto& event : m_pools[pool_idx].items) { + event->Finish(); + } + } + } std::shared_ptr* get(size_t location=0) { @@ -98,13 +98,13 @@ class JEventPool { } - void put(std::shared_ptr* item, bool release, size_t location) { + void put(std::shared_ptr* item, bool clear_event, size_t location) { assert(m_pools != nullptr); // If you hit this, you forgot to call init(). - if (release) { + if (clear_event) { // Do any necessary teardown within the item itself - release_item(item); + (*item)->Clear(); } // Consider each location starting with current one @@ -167,9 +167,9 @@ class JEventPool { } } - void push(std::shared_ptr** source, size_t count, bool release, size_t location) { + void push(std::shared_ptr** source, size_t count, bool clear_event, size_t location) { for (size_t i=0; i(); - event->SetFactorySet(new JFactorySet); SECTION("Single-item JEvent::Insert() can be retrieved via JEvent::GetAllChildren()") { auto b = new Base(22); diff --git a/src/programs/unit_tests/Components/JEventProcessorTests.cc b/src/programs/unit_tests/Components/JEventProcessorTests.cc index d0ea3ef97..ced408ab6 100644 --- a/src/programs/unit_tests/Components/JEventProcessorTests.cc +++ b/src/programs/unit_tests/Components/JEventProcessorTests.cc @@ -4,6 +4,9 @@ #include #include +namespace jana::jeventprocessortests { + + struct MyEventProcessor : public JEventProcessor { int init_count = 0; int process_count = 0; @@ -18,14 +21,17 @@ struct MyEventProcessor : public JEventProcessor { (*destroy_count)++; } void Init() override { + REQUIRE(GetApplication() != nullptr); LOG_INFO(GetLogger()) << "Init() called" << LOG_END; init_count++; } void Process(const JEvent&) override { + REQUIRE(GetApplication() != nullptr); process_count++; LOG_INFO(GetLogger()) << "Process() called" << LOG_END; } void Finish() override { + REQUIRE(GetApplication() != nullptr); LOG_INFO(GetLogger()) << "Finish() called" << LOG_END; finish_count++; } @@ -82,3 +88,110 @@ TEST_CASE("JEventProcessor_Exception") { REQUIRE(found_throw == true); } + + +struct SourceWithRunNumberChange : public JEventSource { + SourceWithRunNumberChange() { + SetCallbackStyle(CallbackStyle::ExpertMode); + } + Result Emit(JEvent& event) { + if (GetEmittedEventCount() < 2) { + event.SetRunNumber(48); + } + else { + event.SetRunNumber(49); + } + return Result::Success; + } +}; + + +struct ProcWithExceptionsAndLogging : public JEventProcessor { + Parameter except_on_init {this, "except_on_init", false, "Except on init"}; + Parameter except_on_beginrun {this, "except_on_beginrun", false, "Except on beginrun"}; + Parameter except_on_process {this, "except_on_process", false, "Except on process"}; + Parameter except_on_endrun {this, "except_on_endrun", false, "Except on endrun"}; + Parameter except_on_finish {this, "except_on_finish", false, "Except on finish"}; + + std::vector log; + + void Init() override { + LOG_INFO(GetLogger()) << "ProcWithExceptionsAndLogging::Init" << LOG_END; + log.push_back("init"); + if (*except_on_init) throw std::runtime_error("Mystery"); + } + void BeginRun(const std::shared_ptr&) override { + LOG_INFO(GetLogger()) << "ProcWithExceptionsAndLogging::BeginRun" << LOG_END; + log.push_back("beginrun"); + if (*except_on_beginrun) throw std::runtime_error("Mystery"); + } + void Process(const std::shared_ptr&) override { + LOG_INFO(GetLogger()) << "ProcWithExceptionsAndLogging::Process" << LOG_END; + log.push_back("process"); + if (*except_on_process) throw std::runtime_error("Mystery"); + } + void EndRun() override { + LOG_INFO(GetLogger()) << "ProcWithExceptionsAndLogging::EndRun" << LOG_END; + log.push_back("endrun"); + if (*except_on_endrun) throw std::runtime_error("Mystery"); + } + void Finish() override { + LOG_INFO(GetLogger()) << "ProcWithExceptionsAndLogging::Finish" << LOG_END; + log.push_back("finish"); + if (*except_on_finish) throw std::runtime_error("Mystery"); + } +}; + +TEST_CASE("JEventProcessor_CallbackSequence") { + JApplication app; + auto sut = new ProcWithExceptionsAndLogging; + app.Add(sut); + // JApplication takes ownership of sut, so our pointer will become invalid when JApplication is destroyed + + SECTION("NoRunNumber") { + app.Add(new JEventSource); + app.SetParameterValue("jana:nevents", 2); + app.Run(); + REQUIRE(sut->log.size() == 6); + REQUIRE(sut->log.at(0) == "init"); + REQUIRE(sut->log.at(1) == "beginrun"); + REQUIRE(sut->log.at(2) == "process"); + REQUIRE(sut->log.at(3) == "process"); + REQUIRE(sut->log.at(4) == "endrun"); + REQUIRE(sut->log.at(5) == "finish"); + } + SECTION("ConstantRunNumber") { + app.Add(new SourceWithRunNumberChange); + app.SetParameterValue("jana:nevents", 2); + app.Run(); + REQUIRE(sut->log.size() == 6); + REQUIRE(sut->log.at(0) == "init"); + REQUIRE(sut->log.at(1) == "beginrun"); + REQUIRE(sut->log.at(2) == "process"); + REQUIRE(sut->log.at(3) == "process"); + REQUIRE(sut->log.at(4) == "endrun"); + REQUIRE(sut->log.at(5) == "finish"); + } + SECTION("MultipleRunNumbers") { + app.Add(new SourceWithRunNumberChange); + app.SetParameterValue("jana:nevents", 5); + app.Run(); + REQUIRE(sut->log.size() == 11); + REQUIRE(sut->log.at(0) == "init"); + REQUIRE(sut->log.at(1) == "beginrun"); + REQUIRE(sut->log.at(2) == "process"); + REQUIRE(sut->log.at(3) == "process"); + REQUIRE(sut->log.at(4) == "endrun"); + REQUIRE(sut->log.at(5) == "beginrun"); + REQUIRE(sut->log.at(6) == "process"); + REQUIRE(sut->log.at(7) == "process"); + REQUIRE(sut->log.at(8) == "process"); + REQUIRE(sut->log.at(9) == "endrun"); + REQUIRE(sut->log.at(10) == "finish"); + } +} + + + + +} // namespace jana::jeventprocessortests diff --git a/src/programs/unit_tests/Components/JEventSourceTests.cc b/src/programs/unit_tests/Components/JEventSourceTests.cc index 990a3904b..e0e79d9a4 100644 --- a/src/programs/unit_tests/Components/JEventSourceTests.cc +++ b/src/programs/unit_tests/Components/JEventSourceTests.cc @@ -10,12 +10,13 @@ struct MyEventSource : public JEventSource { size_t events_in_file = 5; void Open() override { + REQUIRE(GetApplication() != nullptr); LOG_INFO(GetLogger()) << "Open() called" << LOG_END; open_count++; } Result Emit(JEvent&) override { emit_count++; - + REQUIRE(GetApplication() != nullptr); if (GetEmittedEventCount() >= events_in_file) { LOG_INFO(GetLogger()) << "Emit() called, returning FailureFinished" << LOG_END; return Result::FailureFinished; @@ -24,6 +25,7 @@ struct MyEventSource : public JEventSource { return Result::Success; } void Close() override { + REQUIRE(GetApplication() != nullptr); LOG_INFO(GetLogger()) << "Close() called" << LOG_END; close_count++; } diff --git a/src/programs/unit_tests/Components/JEventTests.cc b/src/programs/unit_tests/Components/JEventTests.cc index 5e536b5a6..c2c31bd9d 100644 --- a/src/programs/unit_tests/Components/JEventTests.cc +++ b/src/programs/unit_tests/Components/JEventTests.cc @@ -13,7 +13,6 @@ TEST_CASE("JEventInsertTests") { auto event = std::make_shared(); - event->SetFactorySet(new JFactorySet); SECTION("Single-item JEvent::Insert() can be retrieved via JEvent::Get()") { auto input = new FakeJObject(22); @@ -85,7 +84,6 @@ TEST_CASE("JEventInsertTests") { bool deleted = false; obj->deleted = &deleted; JEvent* event_ptr = new JEvent(); - event_ptr->SetFactorySet(new JFactorySet); event_ptr->Insert(obj, "tag"); REQUIRE(deleted == false); delete event_ptr; diff --git a/src/programs/unit_tests/Components/JFactoryDefTagsTests.cc b/src/programs/unit_tests/Components/JFactoryDefTagsTests.cc index a85e93455..20d610f74 100644 --- a/src/programs/unit_tests/Components/JFactoryDefTagsTests.cc +++ b/src/programs/unit_tests/Components/JFactoryDefTagsTests.cc @@ -93,10 +93,7 @@ TEST_CASE("MediumDefTags") { app.Add(new JFactoryGeneratorT); app.Add(new JFactoryGeneratorT); app.SetParameterValue("DEFTAG:deftagstest::Obj", "tagB"); - app.Initialize(); - auto event = std::make_shared(); - auto jcm = app.GetService(); - jcm->configure_event(*event); + auto event = std::make_shared(&app); auto objs = event->Get(); REQUIRE(objs[0]->E == 33.3); } diff --git a/src/programs/unit_tests/Components/JFactoryGeneratorTests.cc b/src/programs/unit_tests/Components/JFactoryGeneratorTests.cc index 6d1212f0f..dcbaa10f1 100644 --- a/src/programs/unit_tests/Components/JFactoryGeneratorTests.cc +++ b/src/programs/unit_tests/Components/JFactoryGeneratorTests.cc @@ -34,9 +34,7 @@ class MyFacGen : public JFactoryGenerator { TEST_CASE("JFactoryGeneratorTests_UserDefined") { JApplication app; app.Add(new MyFacGen()); - app.Initialize(); - auto event = std::make_shared(); - app.GetService()->configure_event(*event); + auto event = std::make_shared(&app); auto data = event->Get(); REQUIRE(data.at(0)->x == 22); diff --git a/src/programs/unit_tests/Components/JFactoryTests.cc b/src/programs/unit_tests/Components/JFactoryTests.cc index 23a620833..be2d681db 100644 --- a/src/programs/unit_tests/Components/JFactoryTests.cc +++ b/src/programs/unit_tests/Components/JFactoryTests.cc @@ -3,6 +3,8 @@ // Subject to the terms in the LICENSE file found in the top-level directory. +#include "JANA/Components/JComponentFwd.h" +#include "JANA/JFactory.h" #include "catch.hpp" #include "JFactoryTests.h" @@ -39,22 +41,22 @@ TEST_CASE("JFactoryTests") { } -/* SECTION("If no factory is present and nothing inserted, GetObjects called") { // The event hasn't been given any DummyFactory, nor has anything been inserted. // Instead, the JEvent knows to go down to the JEventSource. auto event = std::make_shared(); - DummySource sut; + JFactoryTestDummySource sut; + + // Empty factory needed for GetObjects() to work + event->GetFactorySet()->Add(new JFactoryT()); event->SetJEventSource(&sut); - event->SetFactorySet(new JFactorySet()); - auto data = event->Get(""); + auto data = event->Get(""); REQUIRE(data[0]->data == 8); REQUIRE(data[1]->data == 88); } -*/ SECTION("ChangeRun called only when run number changes") { auto event = std::make_shared(); JFactoryTestDummyFactory sut; @@ -249,6 +251,54 @@ TEST_CASE("JFactory_Exception") { REQUIRE(found_throw == true); } +struct ExceptingInitFactory : public JFactoryT { + void Init() override { + throw std::runtime_error("Exception in Init"); + } + void Process(const std::shared_ptr&) override { + throw std::runtime_error("Exception in Process"); + } +}; + +TEST_CASE("JFactoryTests_ExceptingInitCalledTwice") { + JApplication app; + app.SetParameterValue("jana:loglevel", "error"); + app.Add(new JFactoryGeneratorT()); + auto event = std::make_shared(&app); + + bool found_throw = false; + try { + event->Get(); + } + catch(JException& ex) { + LOG << ex << LOG_END; + REQUIRE(ex.function_name == "JFactory::Init"); + REQUIRE(ex.message == "Exception in Init"); + REQUIRE(ex.exception_type == "std::runtime_error"); + REQUIRE(ex.type_name == "ExceptingInitFactory"); + REQUIRE(ex.instance_name == "JFactoryTestDummyObject"); + found_throw = true; + } + REQUIRE(found_throw == true); + + // Second time around should except from Init again, NOT Process() + found_throw = false; + try { + event->Get(); + } + catch(JException& ex) { + LOG << ex << LOG_END; + REQUIRE(ex.function_name == "JFactory::Init"); + REQUIRE(ex.message == "Exception in Init"); + REQUIRE(ex.exception_type == "std::runtime_error"); + REQUIRE(ex.type_name == "ExceptingInitFactory"); + REQUIRE(ex.instance_name == "JFactoryTestDummyObject"); + found_throw = true; + } + REQUIRE(found_throw == true); +} + + struct MyLoggedFactory : public JFactoryT { MyLoggedFactory() { SetPrefix("myfac"); @@ -269,3 +319,248 @@ TEST_CASE("JFactory_Logger") { app.Run(); } +std::vector factory_with_finish_log; + +struct SourceWithRunNumberChange : public JEventSource { + SourceWithRunNumberChange() { + SetCallbackStyle(CallbackStyle::ExpertMode); + } + Result Emit(JEvent& event) { + if (GetEmittedEventCount() < 2) { + event.SetRunNumber(48); + } + else { + event.SetRunNumber(49); + } + return Result::Success; + } +}; + +struct FactoryWithFinish : public JFactoryT { + Parameter except_on_init {this, "except_on_init", false, "Except on init"}; + Parameter except_on_beginrun {this, "except_on_beginrun", false, "Except on beginrun"}; + Parameter except_on_process {this, "except_on_process", false, "Except on process"}; + Parameter except_on_endrun {this, "except_on_endrun", false, "Except on endrun"}; + Parameter except_on_finish {this, "except_on_finish", false, "Except on finish"}; + + void Init() override { + LOG_INFO(GetLogger()) << "FactoryWithFinish::Init: " << this << LOG_END; + factory_with_finish_log.push_back("init"); + if (*except_on_init) throw std::runtime_error("Mystery"); + } + void BeginRun(const std::shared_ptr&) override { + LOG_INFO(GetLogger()) << "FactoryWithFinish::BeginRun: " << this << LOG_END; + factory_with_finish_log.push_back("beginrun"); + if (*except_on_beginrun) throw std::runtime_error("Mystery"); + } + void Process(const std::shared_ptr&) override { + LOG_INFO(GetLogger()) << "FactoryWithFinish::Process: " << this << LOG_END; + factory_with_finish_log.push_back("process"); + if (*except_on_process) throw std::runtime_error("Mystery"); + } + void EndRun() override { + LOG_INFO(GetLogger()) << "FactoryWithFinish::EndRun: " << this << LOG_END; + factory_with_finish_log.push_back("endrun"); + if (*except_on_endrun) throw std::runtime_error("Mystery"); + } + void Finish() override { + LOG_INFO(GetLogger()) << "FactoryWithFinish::Finish: " << this << LOG_END; + factory_with_finish_log.push_back("finish"); + if (*except_on_finish) throw std::runtime_error("Mystery"); + } +}; + +TEST_CASE("JFactory_CallbackSequence") { + JApplication app; + app.Add(new JFactoryGeneratorT()); + app.SetParameterValue("autoactivate", "JFactoryTestDummyObject"); + app.SetParameterValue("jana:event_pool_size", 1); + + SECTION("NoRunNumber") { + app.Add(new JEventSource); + app.SetParameterValue("jana:nevents", 2); + app.Initialize(); // This init()s a throwaway JFactoryT, which we immediately clear from the log + factory_with_finish_log.clear(); + app.Run(); + REQUIRE(factory_with_finish_log.size() == 6); + REQUIRE(factory_with_finish_log.at(0) == "init"); + REQUIRE(factory_with_finish_log.at(1) == "beginrun"); + REQUIRE(factory_with_finish_log.at(2) == "process"); + REQUIRE(factory_with_finish_log.at(3) == "process"); + REQUIRE(factory_with_finish_log.at(4) == "endrun"); + REQUIRE(factory_with_finish_log.at(5) == "finish"); + } + SECTION("ConstantRunNumber") { + app.Add(new SourceWithRunNumberChange); + app.SetParameterValue("jana:nevents", 2); + app.Initialize(); // This init()s a throwaway JFactoryT, which we immediately clear from the log + factory_with_finish_log.clear(); + app.Run(); + REQUIRE(factory_with_finish_log.size() == 6); + REQUIRE(factory_with_finish_log.at(0) == "init"); + REQUIRE(factory_with_finish_log.at(1) == "beginrun"); + REQUIRE(factory_with_finish_log.at(2) == "process"); + REQUIRE(factory_with_finish_log.at(3) == "process"); + REQUIRE(factory_with_finish_log.at(4) == "endrun"); + REQUIRE(factory_with_finish_log.at(5) == "finish"); + } + SECTION("MultipleRunNumbers") { + app.Add(new SourceWithRunNumberChange); + app.SetParameterValue("jana:nevents", 5); + app.Initialize(); // This init()s a throwaway JFactoryT, which we immediately clear from the log + factory_with_finish_log.clear(); + app.Run(); + REQUIRE(factory_with_finish_log.size() == 11); + REQUIRE(factory_with_finish_log.at(0) == "init"); + REQUIRE(factory_with_finish_log.at(1) == "beginrun"); + REQUIRE(factory_with_finish_log.at(2) == "process"); + REQUIRE(factory_with_finish_log.at(3) == "process"); + REQUIRE(factory_with_finish_log.at(4) == "endrun"); + REQUIRE(factory_with_finish_log.at(5) == "beginrun"); + REQUIRE(factory_with_finish_log.at(6) == "process"); + REQUIRE(factory_with_finish_log.at(7) == "process"); + REQUIRE(factory_with_finish_log.at(8) == "process"); + REQUIRE(factory_with_finish_log.at(9) == "endrun"); + REQUIRE(factory_with_finish_log.at(10) == "finish"); + } +} + +TEST_CASE("JFactory_ExceptionHandling") { + JApplication app; + app.Add(new JFactoryGeneratorT()); + app.SetParameterValue("autoactivate", "JFactoryTestDummyObject"); + app.SetParameterValue("jana:event_pool_size", 1); + + SECTION("ExceptOnInit") { + app.Add(new SourceWithRunNumberChange); + app.SetParameterValue("jana:nevents", 2); + app.SetParameterValue("JFactoryTestDummyObject:except_on_init", true); + app.Initialize(); // This init()s a throwaway JFactoryT, which we immediately clear from the log + factory_with_finish_log.clear(); + bool found_throw = false; + try { + app.Run(); + } + catch(JException& ex) { + LOG << ex << LOG_END; + REQUIRE(ex.function_name == "JFactory::Init"); + REQUIRE(ex.message == "Mystery"); + REQUIRE(ex.exception_type == "std::runtime_error"); + REQUIRE(ex.type_name == "FactoryWithFinish"); + REQUIRE(ex.instance_name == "JFactoryTestDummyObject"); + found_throw = true; + } + REQUIRE(found_throw == true); + REQUIRE(factory_with_finish_log.size() == 1); + REQUIRE(factory_with_finish_log.at(0) == "init"); + } + + SECTION("ExceptOnBeginRun") { + app.Add(new SourceWithRunNumberChange); + app.SetParameterValue("jana:nevents", 2); + app.SetParameterValue("JFactoryTestDummyObject:except_on_beginrun", true); + app.Initialize(); // This init()s a throwaway JFactoryT, which we immediately clear from the log + factory_with_finish_log.clear(); + bool found_throw = false; + try { + app.Run(); + } + catch(JException& ex) { + LOG << ex << LOG_END; + REQUIRE(ex.function_name == "JFactory::BeginRun"); + REQUIRE(ex.message == "Mystery"); + REQUIRE(ex.exception_type == "std::runtime_error"); + REQUIRE(ex.type_name == "FactoryWithFinish"); + REQUIRE(ex.instance_name == "JFactoryTestDummyObject"); + found_throw = true; + } + REQUIRE(found_throw == true); + REQUIRE(factory_with_finish_log.size() == 2); + REQUIRE(factory_with_finish_log.at(0) == "init"); + REQUIRE(factory_with_finish_log.at(1) == "beginrun"); + } + + SECTION("ExceptOnProcess") { + app.Add(new SourceWithRunNumberChange); + app.SetParameterValue("jana:nevents", 2); + app.SetParameterValue("JFactoryTestDummyObject:except_on_process", true); + app.Initialize(); // This init()s a throwaway JFactoryT, which we immediately clear from the log + factory_with_finish_log.clear(); + bool found_throw = false; + try { + app.Run(); + } + catch(JException& ex) { + LOG << ex << LOG_END; + REQUIRE(ex.function_name == "JFactory::Process"); + REQUIRE(ex.message == "Mystery"); + REQUIRE(ex.exception_type == "std::runtime_error"); + REQUIRE(ex.type_name == "FactoryWithFinish"); + REQUIRE(ex.instance_name == "JFactoryTestDummyObject"); + found_throw = true; + } + REQUIRE(found_throw == true); + REQUIRE(factory_with_finish_log.size() == 3); + REQUIRE(factory_with_finish_log.at(0) == "init"); + REQUIRE(factory_with_finish_log.at(1) == "beginrun"); + REQUIRE(factory_with_finish_log.at(2) == "process"); + } + + SECTION("ExceptOnEndRun") { + app.Add(new SourceWithRunNumberChange); + app.SetParameterValue("jana:nevents", 2); + app.SetParameterValue("JFactoryTestDummyObject:except_on_endrun", true); + app.Initialize(); // This init()s a throwaway JFactoryT, which we immediately clear from the log + factory_with_finish_log.clear(); + bool found_throw = false; + try { + app.Run(); + } + catch(JException& ex) { + LOG << ex << LOG_END; + REQUIRE(ex.function_name == "JFactory::EndRun"); + REQUIRE(ex.message == "Mystery"); + REQUIRE(ex.exception_type == "std::runtime_error"); + REQUIRE(ex.type_name == "FactoryWithFinish"); + REQUIRE(ex.instance_name == "JFactoryTestDummyObject"); + found_throw = true; + } + REQUIRE(found_throw == true); + REQUIRE(factory_with_finish_log.size() == 5); + REQUIRE(factory_with_finish_log.at(0) == "init"); + REQUIRE(factory_with_finish_log.at(1) == "beginrun"); + REQUIRE(factory_with_finish_log.at(2) == "process"); + REQUIRE(factory_with_finish_log.at(3) == "process"); + REQUIRE(factory_with_finish_log.at(4) == "endrun"); + } + SECTION("ExceptOnFinish") { + app.Add(new SourceWithRunNumberChange); + app.SetParameterValue("jana:nevents", 2); + app.SetParameterValue("JFactoryTestDummyObject:except_on_finish", true); + app.Initialize(); // This init()s a throwaway JFactoryT, which we immediately clear from the log + factory_with_finish_log.clear(); + bool found_throw = false; + try { + app.Run(); + } + catch(JException& ex) { + LOG << ex << LOG_END; + REQUIRE(ex.function_name == "JFactory::Finish"); + REQUIRE(ex.message == "Mystery"); + REQUIRE(ex.exception_type == "std::runtime_error"); + REQUIRE(ex.type_name == "FactoryWithFinish"); + REQUIRE(ex.instance_name == "JFactoryTestDummyObject"); + found_throw = true; + } + REQUIRE(found_throw == true); + REQUIRE(factory_with_finish_log.size() == 6); + REQUIRE(factory_with_finish_log.at(0) == "init"); + REQUIRE(factory_with_finish_log.at(1) == "beginrun"); + REQUIRE(factory_with_finish_log.at(2) == "process"); + REQUIRE(factory_with_finish_log.at(3) == "process"); + REQUIRE(factory_with_finish_log.at(4) == "endrun"); + REQUIRE(factory_with_finish_log.at(5) == "finish"); + } +} + + diff --git a/src/programs/unit_tests/Components/JFactoryTests.h b/src/programs/unit_tests/Components/JFactoryTests.h index eb3c98801..e700b86c4 100644 --- a/src/programs/unit_tests/Components/JFactoryTests.h +++ b/src/programs/unit_tests/Components/JFactoryTests.h @@ -71,6 +71,7 @@ struct JFactoryTestDummySource: public JEventSource { JFactoryTestDummySource() { SetCallbackStyle(CallbackStyle::ExpertMode); + EnableGetObjects(); } Result Emit(JEvent&) override { diff --git a/src/programs/unit_tests/Components/JMultiFactoryTests.cc b/src/programs/unit_tests/Components/JMultiFactoryTests.cc index bba559bbc..df201fb1f 100644 --- a/src/programs/unit_tests/Components/JMultiFactoryTests.cc +++ b/src/programs/unit_tests/Components/JMultiFactoryTests.cc @@ -37,6 +37,7 @@ struct MyMultifactory : public JMultifactory { } void Process(const std::shared_ptr&) override { + REQUIRE(GetApplication() != nullptr); m_process_call_count += 1; std::vector as; std::vector bs; @@ -81,10 +82,7 @@ TEST_CASE("MultiFactoryTests") { SECTION("Multifactories work with JFactoryGeneratorT") { app.Add(new JFactoryGeneratorT()); - app.Initialize(); - auto jcm = app.GetService(); auto event = std::make_shared(&app); - jcm->configure_event(*event); auto as = event->Get("first"); REQUIRE(as.size() == 2); REQUIRE(as[1]->x == 5.5); @@ -92,10 +90,7 @@ TEST_CASE("MultiFactoryTests") { SECTION("Test that multifactory Process() is only called once") { app.Add(new JFactoryGeneratorT()); - app.Initialize(); - auto jcm = app.GetService(); auto event = std::make_shared(&app); - jcm->configure_event(*event); auto helper_fac = dynamic_cast*>(event->GetFactory("first")); REQUIRE(helper_fac != nullptr); diff --git a/src/programs/unit_tests/Components/PodioTests.cc b/src/programs/unit_tests/Components/PodioTests.cc index c0bedb560..50b0a0d4a 100644 --- a/src/programs/unit_tests/Components/PodioTests.cc +++ b/src/programs/unit_tests/Components/PodioTests.cc @@ -4,6 +4,7 @@ #include #include #include +#include namespace podiotests { @@ -150,11 +151,9 @@ struct TestFac : public JFactoryPodioT { TEST_CASE("JFactoryPodioT::Init gets called") { JApplication app; + app.Add(new JFactoryGeneratorT()); auto event = std::make_shared(&app); - auto fs = new JFactorySet; - fs->Add(new jana2_tests_podiotests_init::TestFac); - event->SetFactorySet(fs); - event->GetFactorySet()->Release(); // Simulate a trip to the JEventPool + event->Clear(); // Simulate a trip to the JEventPool auto r = event->GetCollectionBase("clusters"); REQUIRE(r != nullptr); diff --git a/src/programs/unit_tests/Services/JParameterManagerTests.cc b/src/programs/unit_tests/Services/JParameterManagerTests.cc index 8c75bf0a1..4d0d96019 100644 --- a/src/programs/unit_tests/Services/JParameterManagerTests.cc +++ b/src/programs/unit_tests/Services/JParameterManagerTests.cc @@ -432,6 +432,49 @@ TEST_CASE("JParameterManager_Strictness") { +TEST_CASE("JParameterManager_ConflictingDefaults") { + + int x1 = 3; + int x2 = 4; + int x3 = 3; + int x4 = 4; + + JParameterManager sut; + sut.SetLogger(JLogger()); + + // Simulate a FactorySet containing two JFactories, each of which declares the same parameter with different default values + auto p1 = sut.SetDefaultParameter("my_param_name", x1, "Tests how conflicting defaults are handled"); + REQUIRE(p1->HasDefault() == true); + REQUIRE(p1->IsDefault() == true); + REQUIRE(p1->GetDefault() == "3"); // Should be the _latest_ default found + REQUIRE(p1->GetValue() == "3"); // Should be the _latest_ default value + REQUIRE(x1 == 3); + auto p2 = sut.SetDefaultParameter("my_param_name", x2, "Tests how conflicting defaults are handled"); + REQUIRE(p2->HasDefault() == true); + REQUIRE(p2->IsDefault() == true); + REQUIRE(p2->GetDefault() == "4"); // Should be the _latest_ default found + REQUIRE(p2->GetValue() == "4"); // Should be the _latest_ default value + REQUIRE(x2 == 4); + + // Simulate a _second_ FactorySet containing fresh instances of the same two JFactories, + auto p3 = sut.SetDefaultParameter("my_param_name", x3, "Tests how conflicting defaults are handled"); + REQUIRE(p3->HasDefault() == true); + REQUIRE(p3->IsDefault() == true); + REQUIRE(p3->GetDefault() == "3"); // Should be the _latest_ default found + REQUIRE(p3->GetValue() == "3"); // Should be the _latest_ default value + REQUIRE(x3 == 3); + auto p4 = sut.SetDefaultParameter("my_param_name", x4, "Tests how conflicting defaults are handled"); + REQUIRE(p4->HasDefault() == true); + REQUIRE(p4->IsDefault() == true); + REQUIRE(p4->GetDefault() == "4"); // Should be the _latest_ default value found + REQUIRE(p4->GetValue() == "4"); // Should be the _latest_ default value + REQUIRE(x4 == 4); + + sut.PrintParameters(2,1); +} + + + diff --git a/src/programs/unit_tests/Utils/JCallGraphRecorderTests.cc b/src/programs/unit_tests/Utils/JCallGraphRecorderTests.cc index 4196a5347..8bfa76c0c 100644 --- a/src/programs/unit_tests/Utils/JCallGraphRecorderTests.cc +++ b/src/programs/unit_tests/Utils/JCallGraphRecorderTests.cc @@ -5,6 +5,7 @@ #include #include #include "JANA/JEvent.h" +#include "JANA/JFactoryGenerator.h" TEST_CASE("Test topological sort algorithm in isolation") { @@ -68,13 +69,11 @@ struct FacD: public JFactoryT { TEST_CASE("Test topological sort algorithm using actual Factories") { JApplication app; - JFactorySet* factories = new JFactorySet; - factories->Add(new FacA()); - factories->Add(new FacB()); - factories->Add(new FacC()); - factories->Add(new FacD()); + app.Add(new JFactoryGeneratorT()); + app.Add(new JFactoryGeneratorT()); + app.Add(new JFactoryGeneratorT()); + app.Add(new JFactoryGeneratorT()); auto event = std::make_shared(&app); - event->SetFactorySet(factories); event->GetJCallGraphRecorder()->SetEnabled(); event->Get(); auto result = event->GetJCallGraphRecorder()->TopologicalSort();