From 06931b7640f9d0c04205838fcb834491b8a697a4 Mon Sep 17 00:00:00 2001 From: Rainer Kartmann <rainer.kartmann@kit.edu> Date: Tue, 29 Aug 2023 18:34:38 +0200 Subject: [PATCH] Add hasInstance(), hasSnapshot(), findLatestInstance(), findLatestSnapshot() without arguments --- .../libraries/armem/core/base/EntityBase.h | 47 +-- .../armem/core/base/EntitySnapshotBase.h | 7 + .../armem/core/base/detail/iteration_mixins.h | 150 ++++---- .../armem/core/base/detail/lookup_mixins.h | 206 ++++++++++- .../libraries/armem/core/error/ArMemError.cpp | 19 + .../libraries/armem/core/error/ArMemError.h | 18 + .../libraries/armem/test/ArMemGetFindTest.cpp | 341 ++++++++++++++---- 7 files changed, 595 insertions(+), 193 deletions(-) diff --git a/source/RobotAPI/libraries/armem/core/base/EntityBase.h b/source/RobotAPI/libraries/armem/core/base/EntityBase.h index ed76ae911..e44b1af6b 100644 --- a/source/RobotAPI/libraries/armem/core/base/EntityBase.h +++ b/source/RobotAPI/libraries/armem/core/base/EntityBase.h @@ -24,7 +24,7 @@ namespace armarx::armem::base * @brief An entity over a period of time. * * An entity should be a physical thing or abstract concept existing - * (and potentially evolving) over some time. + * (and potentially evolving) over time. * * Examples are: * - objects (the green box) @@ -43,7 +43,9 @@ namespace armarx::armem::base class EntityBase : public detail::MemoryContainerBase<std::map<Time, _EntitySnapshotT>, _Derived>, public detail::ForEachEntityInstanceMixin<_Derived>, - public detail::GetFindInstanceMixin<_Derived> + public detail::GetFindInstanceMixin<_Derived>, + public detail::GetLatestInstanceMixin<_Derived>, + public detail::GetLatestSnapshotMixin<_Derived> { using Base = detail::MemoryContainerBase<std::map<Time, _EntitySnapshotT>, _Derived>; @@ -99,15 +101,24 @@ namespace armarx::armem::base return this->id().entityName; } - // Has child by key - /// Indicates whether a history entry for the given time exists. + // Has any child + /// Indicate any snapshot exists. + bool + hasSnapshot() const + { + return not this->empty(); + } + + // Has child with key + /// Indicate whether a snapshot at the given time exists. bool hasSnapshot(const Time& time) const { return this->findSnapshot(time) != nullptr; } - // Has child by MemoryID + // Has child with MemoryID + /// Indicate whether a snapshot with the given ID exists. bool hasSnapshot(const MemoryID& snapshotID) const { @@ -226,31 +237,6 @@ namespace armarx::armem::base return this->empty() ? nullptr : &this->_container.rbegin()->second; } - /** - * @brief Return the snapshot with the most recent timestamp. - * @return The latest snapshot. - * @throw `armem::error::EntityHistoryEmpty` If the history is empty. - */ - EntitySnapshotT& - getLatestSnapshot() - { - return const_cast<EntitySnapshotT&>( - const_cast<const EntityBase*>(this)->getLatestSnapshot()); - } - - const EntitySnapshotT& - getLatestSnapshot() const - { - if (const EntitySnapshotT* snapshot = this->findLatestSnapshot()) - { - return *snapshot; - } - else - { - throw armem::error::EntityHistoryEmpty(name(), "when getting the latest snapshot."); - } - } - /** * @brief Return the snapshot with the least recent timestamp. * @return The first snapshot or nullptr if the entity is empty. @@ -380,6 +366,7 @@ namespace armarx::armem::base return snapshot ? snapshot->findInstance(instanceIndex) : nullptr; } + #if 0 // Do not offer this yet. auto* findLatestInstanceData(int instanceIndex = 0) { diff --git a/source/RobotAPI/libraries/armem/core/base/EntitySnapshotBase.h b/source/RobotAPI/libraries/armem/core/base/EntitySnapshotBase.h index 614393f4a..a2412f4c8 100644 --- a/source/RobotAPI/libraries/armem/core/base/EntitySnapshotBase.h +++ b/source/RobotAPI/libraries/armem/core/base/EntitySnapshotBase.h @@ -67,6 +67,13 @@ namespace armarx::armem::base return this->id().timestamp; } + /// Indicate whether this snapshot has any instance. + bool + hasInstance() const + { + return not this->empty(); + } + // Has child by key bool hasInstance(int index) const diff --git a/source/RobotAPI/libraries/armem/core/base/detail/iteration_mixins.h b/source/RobotAPI/libraries/armem/core/base/detail/iteration_mixins.h index b686b1cc7..4945da9a9 100644 --- a/source/RobotAPI/libraries/armem/core/base/detail/iteration_mixins.h +++ b/source/RobotAPI/libraries/armem/core/base/detail/iteration_mixins.h @@ -5,13 +5,12 @@ #include <RobotAPI/libraries/armem/core/MemoryID.h> - namespace { - template<typename F, typename Ret, typename A, typename... Rest> + template <typename F, typename Ret, typename A, typename... Rest> A helper(Ret (F::*)(A, Rest...)); - template<typename F, typename Ret, typename A, typename... Rest> + template <typename F, typename Ret, typename A, typename... Rest> A helper(Ret (F::*)(A, Rest...) const); // volatile or lvalue/rvalue *this not required for lambdas (phew) @@ -19,12 +18,12 @@ namespace template <typename FuncT> struct first_argument { - using type = decltype( helper(&FuncT::operator()) ); + using type = decltype(helper(&FuncT::operator())); }; template <typename FuncT> using first_argument_t = typename first_argument<FuncT>::type; -} +} // namespace namespace armarx::armem::base::detail { @@ -33,9 +32,10 @@ namespace armarx::armem::base::detail // Handle functions with different return type. template <class FunctionT, class ChildT> - bool call(FunctionT&& func, ChildT&& child) + bool + call(FunctionT&& func, ChildT&& child) { - if constexpr(std::is_same_v<decltype(func(child)), bool>) + if constexpr (std::is_same_v<decltype(func(child)), bool>) { if (!func(child)) { @@ -50,10 +50,10 @@ namespace armarx::armem::base::detail } } - // Single-valued containers. template <class ContainerT, class FunctionT> - bool forEachChildSingle(ContainerT& container, FunctionT&& func) + bool + forEachChildSingle(ContainerT& container, FunctionT&& func) { for (auto& child : container) { @@ -65,10 +65,10 @@ namespace armarx::armem::base::detail return true; } - // Pair-valued containers. template <class ContainerT, class FunctionT> - bool forEachChildPair(ContainerT& container, FunctionT&& func) + bool + forEachChildPair(ContainerT& container, FunctionT&& func) { for (auto& [_, child] : container) { @@ -80,22 +80,25 @@ namespace armarx::armem::base::detail return true; } - // see: https://en.cppreference.com/w/cpp/types/void_t // primary template handles types that have no nested ::type member: - template< class, class = void > - struct has_mapped_type : std::false_type { }; + template <class, class = void> + struct has_mapped_type : std::false_type + { + }; // specialization recognizes types that do have a nested ::type member: - template< class T > - struct has_mapped_type<T, std::void_t<typename T::mapped_type>> : std::true_type { }; - + template <class T> + struct has_mapped_type<T, std::void_t<typename T::mapped_type>> : std::true_type + { + }; template <class ContainerT, class FunctionT> - bool forEachChild(ContainerT& container, FunctionT&& func) + bool + forEachChild(ContainerT& container, FunctionT&& func) { - if constexpr(has_mapped_type<ContainerT>::value) + if constexpr (has_mapped_type<ContainerT>::value) { return forEachChildPair(container, func); } @@ -105,20 +108,15 @@ namespace armarx::armem::base::detail } } - template <class FunctionT, class ParentT, class ChildT> - bool forEachInstanceIn( - const MemoryID& id, - FunctionT&& func, - ParentT& parent, - bool single, - ChildT* child - ) + bool + forEachInstanceIn(const MemoryID& id, + FunctionT&& func, + ParentT& parent, + bool single, + ChildT* child) { - auto childFn = [&id,&func](auto& child) - { - return child.forEachInstanceIn(id, func); - }; + auto childFn = [&id, &func](auto& child) { return child.forEachInstanceIn(id, func); }; if (single) { return child ? childFn(*child) : true; @@ -129,8 +127,6 @@ namespace armarx::armem::base::detail } } - - // We use auto instead of, e.g. DerivedT::EntitySnapshotT, // as we cannot use the typedef before DerivedT was completely defined. @@ -143,26 +139,22 @@ namespace armarx::armem::base::detail * @param func Function like: bool process(EntityInstanceT& instance)> */ template <class InstanceFunctionT> - bool forEachInstance(InstanceFunctionT&& func) + bool + forEachInstance(InstanceFunctionT&& func) { return static_cast<DerivedT*>(this)->forEachSnapshot( - [&func](auto & snapshot) -> bool - { - return snapshot.forEachInstance(func); - }); + [&func](auto& snapshot) -> bool { return snapshot.forEachInstance(func); }); } /** * @param func Function like: bool process(const EntityInstanceT& instance) */ template <class InstanceFunctionT> - bool forEachInstance(InstanceFunctionT&& func) const + bool + forEachInstance(InstanceFunctionT&& func) const { return static_cast<const DerivedT*>(this)->forEachSnapshot( - [&func](const auto & snapshot) -> bool - { - return snapshot.forEachInstance(func); - }); + [&func](const auto& snapshot) -> bool { return snapshot.forEachInstance(func); }); } /** @@ -180,19 +172,19 @@ namespace armarx::armem::base::detail * @param func Function like: `bool process(const my::arondto::CoolData& data)` */ template <class AronDtoFunctionT> - bool forEachInstanceAs(AronDtoFunctionT&& func) const + bool + forEachInstanceAs(AronDtoFunctionT&& func) const { return static_cast<const DerivedT*>(this)->forEachInstance( - [&func](const auto & instance) -> bool - { - using AronDtoT = typename std::remove_reference_t<first_argument_t<AronDtoFunctionT>>; - return func(instance.template dataAs<AronDtoT>()); - }); + [&func](const auto& instance) -> bool + { + using AronDtoT = + typename std::remove_reference_t<first_argument_t<AronDtoFunctionT>>; + return func(instance.template dataAs<AronDtoT>()); + }); } - }; - template <class DerivedT> struct ForEachEntitySnapshotMixin { @@ -200,30 +192,25 @@ namespace armarx::armem::base::detail * @param func Function like: bool process(EntitySnapshotT& snapshot)> */ template <class SnapshotFunctionT> - bool forEachSnapshot(SnapshotFunctionT&& func) + bool + forEachSnapshot(SnapshotFunctionT&& func) { return static_cast<DerivedT*>(this)->forEachEntity( - [&func](auto & entity) -> bool - { - return entity.forEachSnapshot(func); - }); + [&func](auto& entity) -> bool { return entity.forEachSnapshot(func); }); } /** * @param func Function like: bool process(const EntitySnapshotT& snapshot) */ template <class SnapshotFunctionT> - bool forEachSnapshot(SnapshotFunctionT&& func) const + bool + forEachSnapshot(SnapshotFunctionT&& func) const { return static_cast<const DerivedT*>(this)->forEachEntity( - [&func](const auto & entity) -> bool - { - return entity.forEachSnapshot(func); - }); + [&func](const auto& entity) -> bool { return entity.forEachSnapshot(func); }); } }; - template <class DerivedT> struct ForEachEntityMixin { @@ -231,30 +218,27 @@ namespace armarx::armem::base::detail * @param func Function like: bool process(EntityT& entity)> */ template <class FunctionT> - bool forEachEntity(FunctionT&& func) + bool + forEachEntity(FunctionT&& func) { return static_cast<DerivedT*>(this)->forEachProviderSegment( - [&func](auto & providerSegment) -> bool - { - return providerSegment.forEachEntity(func); - }); + [&func](auto& providerSegment) -> bool + { return providerSegment.forEachEntity(func); }); } /** * @param func Function like: bool process(const EntityT& entity) */ template <class FunctionT> - bool forEachEntity(FunctionT&& func) const + bool + forEachEntity(FunctionT&& func) const { return static_cast<const DerivedT*>(this)->forEachProviderSegment( - [&func](const auto & providerSegment) -> bool - { - return providerSegment.forEachEntity(func); - }); + [&func](const auto& providerSegment) -> bool + { return providerSegment.forEachEntity(func); }); } }; - template <class DerivedT> struct ForEachProviderSegmentMixin { @@ -262,27 +246,25 @@ namespace armarx::armem::base::detail * @param func Function like: bool process(ProviderSegmentT& providerSegment)> */ template <class FunctionT> - bool forEachProviderSegment(FunctionT&& func) + bool + forEachProviderSegment(FunctionT&& func) { return static_cast<DerivedT*>(this)->forEachCoreSegment( - [&func](auto & coreSegment) -> bool - { - return coreSegment.forEachProviderSegment(func); - }); + [&func](auto& coreSegment) -> bool + { return coreSegment.forEachProviderSegment(func); }); } /** * @param func Function like: bool process(const ProviderSegmentT& providerSegment) */ template <class FunctionT> - bool forEachProviderSegment(FunctionT&& func) const + bool + forEachProviderSegment(FunctionT&& func) const { return static_cast<const DerivedT*>(this)->forEachCoreSegment( - [&func](const auto & coreSegment) -> bool - { - return coreSegment.forEachProviderSegment(func); - }); + [&func](const auto& coreSegment) -> bool + { return coreSegment.forEachProviderSegment(func); }); } }; -} +} // namespace armarx::armem::base::detail diff --git a/source/RobotAPI/libraries/armem/core/base/detail/lookup_mixins.h b/source/RobotAPI/libraries/armem/core/base/detail/lookup_mixins.h index ce63b1ee7..618b8e9fc 100644 --- a/source/RobotAPI/libraries/armem/core/base/detail/lookup_mixins.h +++ b/source/RobotAPI/libraries/armem/core/base/detail/lookup_mixins.h @@ -51,8 +51,30 @@ namespace armarx::armem::base::detail template <class DerivedT> struct GetFindInstanceMixin { - // Relies on this->find/getSnapshot() + // Relies on this->find/get/forEachInstance() + /** + * @brief Indicate whether this container contains at least one entity instance. + * @return True if there is at least one entity instance in this container. + */ + bool + hasInstance() const + { + bool has = false; + derived<DerivedT>(this).forEachInstance( + [&has](const auto& snapshot) + { + has = true; + return false; + }); + return has; + } + + /** + * @brief Indicate whether this container has an instance with the given ID. + * @param instanceID The instance ID. + * @return ... WIP + */ bool hasInstance(const MemoryID& instanceID) const { @@ -82,7 +104,7 @@ namespace armarx::armem::base::detail * @brief Retrieve an entity instance. * @param id The instance ID. * @return The instance if it is found. - * @throw `armem::error::ArMemError` if it is missing. + * @throw armem::error::ArMemError if it is missing. */ auto& getInstance(const MemoryID& instanceID) @@ -98,20 +120,118 @@ namespace armarx::armem::base::detail }; template <class DerivedT> - struct GetFindSnapshotMixin + struct GetLatestInstanceMixin { - // Relies on this->find/getEntity() + // Relies on findLatestInstance() + + /** + * @brief Retrieve the latest entity instance in this container. + * @param instanceIndex The instance's index in the latest snapshot. + * @return The latest entity instance. + * @throw armem::error::ArMemError If there is no entity instance. + */ + auto& + getLatestInstance(int instanceIndex = 0) + { + auto* instance = derived<DerivedT>(this).findLatestInstance(); + if (not instance) + { + throw armem::error::NoSuchEntries( + "entity instances", DerivedT::getLevelName(), derived<DerivedT>(this).id()); + } + return *instance; + } + const auto& + getLatestInstance(int instanceIndex = 0) const + { + const auto* instance = derived<DerivedT>(this).findLatestInstance(); + if (not instance) + { + throw armem::error::NoSuchEntries( + "entity instances", DerivedT::getLevelName(), derived<DerivedT>(this).id()); + } + return *instance; + } + }; + + template <class DerivedT> + struct GetLatestSnapshotMixin + { + // Relies on findLatestSnapshot() + + /** + * @brief Retrieve the latest entity snapshot in this container. + * @param snapshotIndex The snapshot's index in the latest snapshot. + * @return The latest entity snapshot. + * @throw armem::error::ArMemError If there is no entity snapshot. + */ + auto& + getLatestSnapshot(int snapshotIndex = 0) + { + auto* snapshot = derived<DerivedT>(this).findLatestSnapshot(); + if (not snapshot) + { + throw armem::error::NoSuchEntries( + "entity snapshots", DerivedT::getLevelName(), derived<DerivedT>(this).id()); + } + return *snapshot; + } + + const auto& + getLatestSnapshot(int snapshotIndex = 0) const + { + const auto* snapshot = derived<DerivedT>(this).findLatestSnapshot(); + if (not snapshot) + { + throw armem::error::NoSuchEntries( + "entity snapshots", DerivedT::getLevelName(), derived<DerivedT>(this).id()); + } + return *snapshot; + } + }; + + template <class DerivedT> + struct GetFindSnapshotMixin : + public GetLatestInstanceMixin<DerivedT>, + public GetLatestSnapshotMixin<DerivedT> + { + // Relies on this->find/getEntity, forEachSnapshot() + + /** + * @brief Indicate whether this container contains at least one entity snapshot. + * @return True if there is at least one entity snapshot in this container. + */ + bool + hasSnapshot() const + { + bool has = false; + derived<DerivedT>(this).forEachSnapshot( + [&has](const auto& snapshot) + { + has = true; + return false; + }); + return has; + } + + /** + * @brief Indicates whether a snapshot with the given ID exists in this container. + * @param snapshotID The snapshot ID. + * @return True if the snapshot exists, false otherwise. + */ bool hasSnapshot(const MemoryID& snapshotID) const { return derived<DerivedT>(this).findSnapshot(snapshotID) != nullptr; } + // Snapshot by ID + /** * @brief Find an entity snapshot. * @param id The snapshot ID. - * @return The snapshot or nullptr if it is missing. + * @return The snapshot, or nullptr if it is missing. */ auto* findSnapshot(const MemoryID& snapshotID) @@ -131,7 +251,7 @@ namespace armarx::armem::base::detail * @brief Retrieve an entity snapshot. * @param id The snapshot ID. * @return The snapshot if it is found. - * @throw `armem::error::ArMemError` if it is missing. + * @throw armem::error::ArMemError if it is missing. */ auto& getSnapshot(const MemoryID& snapshotID) @@ -145,8 +265,14 @@ namespace armarx::armem::base::detail return derived<DerivedT>(this).getEntity(snapshotID).getSnapshot(snapshotID); } - // More elaborate cases + // Latest snapshot in entity + /** + * @brief Find the latest snapshot in the given entity. + * @param entityID The entity's ID. + * @return A pointer to the latest snapshot in the specified entity, or nullptr if the + * entity does not exist or has no snapshot. + */ auto* findLatestSnapshot(const MemoryID& entityID) { @@ -161,6 +287,54 @@ namespace armarx::armem::base::detail return entity ? entity->findLatestSnapshot() : nullptr; } + // Latest snapshot in container. + + /** + * @brief Find the latest snapshot in this container. + * @return A pointer to the latest instance, or nullptr if there is no snapshot. + */ + const auto* + findLatestSnapshot() const + { + const typename DerivedT::EntitySnapshotT* latestSnapshot = nullptr; + derived<DerivedT>(this).forEachEntity( + [&latestSnapshot](const auto& entity) + { + const auto* snapshot = entity.findLatestSnapshot(); + if (latestSnapshot == nullptr) + { + latestSnapshot = snapshot; + } + else if (snapshot and snapshot->time() > latestSnapshot->time()) + { + latestSnapshot = snapshot; + } + }); + return latestSnapshot; + } + + auto* + findLatestSnapshot() + { + typename DerivedT::EntitySnapshotT* latestSnapshot = nullptr; + derived<DerivedT>(this).forEachEntity( + [&latestSnapshot](auto& entity) + { + auto* snapshot = entity.findLatestSnapshot(); + if (latestSnapshot == nullptr) + { + latestSnapshot = snapshot; + } + else if (snapshot and snapshot->time() > latestSnapshot->time()) + { + latestSnapshot = snapshot; + } + }); + return latestSnapshot; + } + + // Latest instance. + auto* findLatestInstance(const MemoryID& entityID, int instanceIndex = 0) { @@ -174,6 +348,20 @@ namespace armarx::armem::base::detail auto* snapshot = derived<DerivedT>(this).findLatestSnapshot(entityID); return snapshot ? snapshot->findInstance(instanceIndex) : nullptr; } + + const auto* + findLatestInstance(int instanceIndex = 0) const + { + auto* snapshot = derived<DerivedT>(this).findLatestSnapshot(); + return snapshot ? snapshot->findInstance(instanceIndex) : nullptr; + } + + auto* + findLatestInstance(int instanceIndex = 0) + { + auto* snapshot = derived<DerivedT>(this).findLatestSnapshot(); + return snapshot ? snapshot->findInstance(instanceIndex) : nullptr; + } }; template <class DerivedT> @@ -210,7 +398,7 @@ namespace armarx::armem::base::detail * @brief Retrieve an entity. * @param id The entity ID. * @return The entity if it is found. - * @throw `armem::error::ArMemError` if it is missing. + * @throw armem::error::ArMemError if it is missing. */ auto& getEntity(const MemoryID& entityID) @@ -259,7 +447,7 @@ namespace armarx::armem::base::detail * @brief Retrieve a provider segment. * @param id The provider segment ID. * @return The provider segment if it is found. - * @throw `armem::error::ArMemError` if it is missing. + * @throw armem::error::ArMemError if it is missing. */ auto& getProviderSegment(const MemoryID& providerSegmentID) diff --git a/source/RobotAPI/libraries/armem/core/error/ArMemError.cpp b/source/RobotAPI/libraries/armem/core/error/ArMemError.cpp index 6197ded3c..354b95712 100644 --- a/source/RobotAPI/libraries/armem/core/error/ArMemError.cpp +++ b/source/RobotAPI/libraries/armem/core/error/ArMemError.cpp @@ -99,6 +99,25 @@ namespace armarx::armem::error return ss.str(); } + NoSuchEntries::NoSuchEntries(const std::string& missingTerm, + const std::string& containerTerm, + const MemoryID& containerID, + const std::string& message) : + ArMemError(makeMsg(missingTerm, containerTerm, containerID, message)) + { + } + + std::string + NoSuchEntries::makeMsg(const std::string& missingTerm, + const std::string& containerTerm, + const MemoryID& containerID, + const std::string& message) + { + std::stringstream ss; + ss << "No " << missingTerm << " in " << containerTerm << " " << containerID << "."; + return ss.str(); + } + MissingData::MissingData(const std::string& missingTerm, const std::string& missingName, const std::string& ownTerm, diff --git a/source/RobotAPI/libraries/armem/core/error/ArMemError.h b/source/RobotAPI/libraries/armem/core/error/ArMemError.h index 20a97a1fc..69ab7d4dd 100644 --- a/source/RobotAPI/libraries/armem/core/error/ArMemError.h +++ b/source/RobotAPI/libraries/armem/core/error/ArMemError.h @@ -98,6 +98,24 @@ namespace armarx::armem::error size_t size); }; + /** + * @brief Indicates that an operation requiring at least one element to exist + * failed because there were no such entries. + */ + class NoSuchEntries : public ArMemError + { + public: + NoSuchEntries(const std::string& missingTerm, + const std::string& containerTerm, + const MemoryID& containerID, + const std::string& message = ""); + + static std::string makeMsg(const std::string& missingTerm, + const std::string& containerTerm, + const MemoryID& containerID, + const std::string& message = ""); + }; + /** * @brief Indicates that a container did have an entry, but the entry's data was * null when trying to access it. diff --git a/source/RobotAPI/libraries/armem/test/ArMemGetFindTest.cpp b/source/RobotAPI/libraries/armem/test/ArMemGetFindTest.cpp index f03b02052..95cf79299 100644 --- a/source/RobotAPI/libraries/armem/test/ArMemGetFindTest.cpp +++ b/source/RobotAPI/libraries/armem/test/ArMemGetFindTest.cpp @@ -14,7 +14,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * * @package RobotAPI::ArmarXObjects::armem - * @author Simon Ottenhaus ( simon dot ottenhaus at kit dot edu ) + * @author Rainer Kartmann ( raier dot kartmann at kit dot edu ) * @date 2020 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt * GNU General Public License @@ -24,29 +24,36 @@ #define ARMARX_BOOST_TEST -#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> -#include <RobotAPI/libraries/armem/core/error.h> -#include <RobotAPI/libraries/armem/core/operations.h> +#include <iostream> #include <RobotAPI/Test.h> - -#include <iostream> +#include <RobotAPI/libraries/armem/core/error.h> +#include <RobotAPI/libraries/armem/core/operations.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> namespace armem = armarx::armem; namespace wm = armarx::armem::wm; namespace aron = armarx::aron; - namespace ArMemGetFindTest { struct Fixture { wm::EntitySnapshot snapshot; + wm::EntitySnapshot snapshotEmpty; + wm::Entity entity; + wm::Entity entityEmpty; + wm::ProviderSegment provSeg; + wm::ProviderSegment provSegEmpty; + wm::CoreSegment coreSeg; + wm::CoreSegment coreSegEmpty; + wm::Memory memory; + wm::Memory memoryEmpty; armem::MemoryID instanceID; armem::MemoryID snapshotID; @@ -58,24 +65,38 @@ namespace ArMemGetFindTest Fixture() { { + snapshotEmpty.time() = armem::Time(armem::Duration::MicroSeconds(500)); + snapshot.time() = armem::Time(armem::Duration::MicroSeconds(1000)); snapshot.addInstance(); } { + entityEmpty.name() = "entity empty"; + entity.name() = "entity"; entity.addSnapshot(snapshot); + entity.addSnapshot(snapshotEmpty); } { + provSegEmpty.name() = "provider segment empty"; + provSeg.name() = "provider segment"; provSeg.addEntity(entity); + provSeg.addEntity(entityEmpty); } { + coreSegEmpty.name() = "core segment empty"; + coreSeg.name() = "core segment"; coreSeg.addProviderSegment(provSeg); + coreSeg.addProviderSegment(provSegEmpty); } { + memoryEmpty.name() = "memory empty"; + memory.name() = "memory"; memory.addCoreSegment(coreSeg); + memory.addCoreSegment(coreSegEmpty); } memoryID = memory.id(); @@ -85,22 +106,18 @@ namespace ArMemGetFindTest snapshotID = entityID.withTimestamp(snapshot.time()); instanceID = snapshotID.withInstanceIndex(0); } + ~Fixture() { } - template <class ParentT> - void test_get_find_instance_by_id(ParentT&& parent) - { - _test_get_find_instance_by_id(const_cast<const ParentT&>(parent)); - _test_get_find_instance_by_id(const_cast<ParentT&>(parent)); - } - template <class ParentT> - void _test_get_find_instance_by_id(ParentT&& parent) + void + _test_get_find_instance_by_id(ParentT&& parent) { BOOST_TEST_CONTEXT("Parent: " << armem::print(parent)) { + BOOST_CHECK_EQUAL(parent.hasInstance(), true); BOOST_CHECK_EQUAL(parent.hasInstance(snapshotID.withInstanceIndex(0)), true); BOOST_CHECK_EQUAL(parent.hasInstance(snapshotID.withInstanceIndex(1)), false); @@ -108,106 +125,240 @@ namespace ArMemGetFindTest BOOST_CHECK_EQUAL(parent.findInstance(snapshotID.withInstanceIndex(1)), nullptr); BOOST_CHECK_NO_THROW(parent.getInstance(snapshotID.withInstanceIndex(0))); - BOOST_CHECK_THROW(parent.getInstance(snapshotID.withInstanceIndex(1)), armem::error::MissingEntry); + BOOST_CHECK_THROW(parent.getInstance(snapshotID.withInstanceIndex(1)), + armem::error::MissingEntry); } } template <class ParentT> - void test_get_find_snapshot_by_id(ParentT&& parent) + void + test_get_find_instance_by_id(ParentT&& parent) { - _test_get_find_snapshot_by_id(const_cast<const ParentT&>(parent)); - _test_get_find_snapshot_by_id(const_cast<ParentT&>(parent)); + _test_get_find_instance_by_id(const_cast<const ParentT&>(parent)); + _test_get_find_instance_by_id(const_cast<ParentT&>(parent)); } + template <class ParentT> - void _test_get_find_snapshot_by_id(ParentT&& parent) + void + _test_get_find_latest_instance(ParentT&& parent) { BOOST_TEST_CONTEXT("Parent: " << armem::print(parent)) { - BOOST_CHECK_EQUAL(parent.hasSnapshot(entityID.withTimestamp(armem::Time(armem::Duration::MicroSeconds(1000)))), true); - BOOST_CHECK_EQUAL(parent.hasSnapshot(entityID.withTimestamp(armem::Time(armem::Duration::MicroSeconds(2000)))), false); + BOOST_CHECK_EQUAL(parent.hasInstance(), true); + BOOST_CHECK_NE(parent.findLatestInstance(), nullptr); + BOOST_CHECK_NO_THROW(parent.getLatestInstance()); + } + } - BOOST_CHECK_NE(parent.findSnapshot(entityID.withTimestamp(armem::Time(armem::Duration::MicroSeconds(1000)))), nullptr); - BOOST_CHECK_EQUAL(parent.findSnapshot(entityID.withTimestamp(armem::Time(armem::Duration::MicroSeconds(2000)))), nullptr); + template <class ParentT> + void + test_get_find_latest_instance(ParentT&& parent) + { + _test_get_find_latest_instance(const_cast<const ParentT&>(parent)); + _test_get_find_latest_instance(const_cast<ParentT&>(parent)); + } - BOOST_CHECK_NO_THROW(parent.getSnapshot(entityID.withTimestamp(armem::Time(armem::Duration::MicroSeconds(1000))))); - BOOST_CHECK_THROW(parent.getSnapshot(entityID.withTimestamp(armem::Time(armem::Duration::MicroSeconds(2000)))), armem::error::MissingEntry); + template <class ParentT> + void + _test_get_find_latest_instance_when_empty(ParentT&& parent) + { + BOOST_TEST_CONTEXT("Parent: " << armem::print(parent)) + { + BOOST_CHECK_EQUAL(parent.hasInstance(), false); + BOOST_CHECK_EQUAL(parent.findLatestInstance(), nullptr); + BOOST_CHECK_THROW(parent.getLatestInstance(), armem::error::NoSuchEntries); } } + template <class ParentT> + void + test_get_find_latest_instance_when_empty(ParentT&& parent) + { + _test_get_find_latest_instance_when_empty(const_cast<const ParentT&>(parent)); + _test_get_find_latest_instance_when_empty(const_cast<ParentT&>(parent)); + } template <class ParentT> - void test_get_find_entity_by_id(ParentT&& parent) + void + _test_get_find_snapshot_by_id(ParentT&& parent) { - _test_get_find_entity_by_id(const_cast<const ParentT&>(parent)); - _test_get_find_entity_by_id(const_cast<ParentT&>(parent)); + BOOST_TEST_CONTEXT("Parent: " << armem::print(parent)) + { + BOOST_CHECK_EQUAL(parent.hasSnapshot(), true); + + BOOST_CHECK_EQUAL(parent.hasSnapshot(entityID.withTimestamp( + armem::Time(armem::Duration::MicroSeconds(1000)))), + true); + BOOST_CHECK_EQUAL(parent.hasSnapshot(entityID.withTimestamp( + armem::Time(armem::Duration::MicroSeconds(2000)))), + false); + + BOOST_CHECK_NE(parent.findSnapshot(entityID.withTimestamp( + armem::Time(armem::Duration::MicroSeconds(1000)))), + nullptr); + BOOST_CHECK_EQUAL(parent.findSnapshot(entityID.withTimestamp( + armem::Time(armem::Duration::MicroSeconds(2000)))), + nullptr); + + BOOST_CHECK_NO_THROW(parent.getSnapshot( + entityID.withTimestamp(armem::Time(armem::Duration::MicroSeconds(1000))))); + BOOST_CHECK_THROW(parent.getSnapshot(entityID.withTimestamp( + armem::Time(armem::Duration::MicroSeconds(2000)))), + armem::error::MissingEntry); + } + } + + template <class ParentT> + void + test_get_find_snapshot_by_id(ParentT&& parent) + { + _test_get_find_snapshot_by_id(const_cast<const ParentT&>(parent)); + _test_get_find_snapshot_by_id(const_cast<ParentT&>(parent)); } + template <class ParentT> - void _test_get_find_entity_by_id(ParentT&& parent) + void + _test_get_find_entity_by_id(ParentT&& parent) { BOOST_TEST_CONTEXT("Parent: " << armem::print(parent)) { BOOST_CHECK_EQUAL(parent.hasEntity(provSegID.withEntityName("entity")), true); - BOOST_CHECK_EQUAL(parent.hasEntity(provSegID.withEntityName("other entity")), false); + BOOST_CHECK_EQUAL(parent.hasEntity(provSegID.withEntityName("other entity")), + false); BOOST_CHECK_NE(parent.findEntity(provSegID.withEntityName("entity")), nullptr); - BOOST_CHECK_EQUAL(parent.findEntity(provSegID.withEntityName("other entity")), nullptr); + BOOST_CHECK_EQUAL(parent.findEntity(provSegID.withEntityName("other entity")), + nullptr); BOOST_CHECK_NO_THROW(parent.getEntity(provSegID.withEntityName("entity"))); - BOOST_CHECK_THROW(parent.getEntity(provSegID.withEntityName("other entity")), armem::error::MissingEntry); + BOOST_CHECK_THROW(parent.getEntity(provSegID.withEntityName("other entity")), + armem::error::MissingEntry); } } template <class ParentT> - void test_get_find_provider_segment_by_id(ParentT&& parent) + void + _test_get_find_latest_snapshot(ParentT&& parent) { - _test_get_find_provider_segment_by_id(const_cast<const ParentT&>(parent)); - _test_get_find_provider_segment_by_id(const_cast<ParentT&>(parent)); + BOOST_TEST_CONTEXT("Parent: " << armem::print(parent)) + { + BOOST_CHECK_EQUAL(parent.hasSnapshot(), true); + BOOST_CHECK_NE(parent.findLatestSnapshot(), nullptr); + BOOST_CHECK_NO_THROW(parent.getLatestSnapshot()); + } } + template <class ParentT> - void _test_get_find_provider_segment_by_id(ParentT&& parent) + void + test_get_find_latest_snapshot(ParentT&& parent) + { + _test_get_find_latest_snapshot(const_cast<const ParentT&>(parent)); + _test_get_find_latest_snapshot(const_cast<ParentT&>(parent)); + } + + template <class ParentT> + void + _test_get_find_latest_snapshot_when_empty(ParentT&& parent) { BOOST_TEST_CONTEXT("Parent: " << armem::print(parent)) { - BOOST_CHECK_EQUAL(parent.hasProviderSegment(provSegID.withProviderSegmentName("provider segment")), true); - BOOST_CHECK_EQUAL(parent.hasProviderSegment(provSegID.withProviderSegmentName("other provider segment")), false); - - BOOST_CHECK_NE(parent.findProviderSegment(provSegID.withProviderSegmentName("provider segment")), nullptr); - BOOST_CHECK_EQUAL(parent.findProviderSegment(provSegID.withProviderSegmentName("other provider segment")), nullptr); - - BOOST_CHECK_NO_THROW(parent.getProviderSegment(provSegID.withProviderSegmentName("provider segment"))); - BOOST_CHECK_THROW(parent.getProviderSegment(provSegID.withProviderSegmentName("other provider segment")), armem::error::MissingEntry); + BOOST_CHECK_EQUAL(parent.hasSnapshot(), false); + BOOST_CHECK_EQUAL(parent.findLatestSnapshot(), nullptr); + BOOST_CHECK_THROW(parent.getLatestSnapshot(), armem::error::NoSuchEntries); } } template <class ParentT> - void test_get_find_core_segment_by_id(ParentT&& parent) + void + test_get_find_latest_snapshot_when_empty(ParentT&& parent) { - _test_get_find_core_segment_by_id(const_cast<const ParentT&>(parent)); - _test_get_find_core_segment_by_id(const_cast<ParentT&>(parent)); + _test_get_find_latest_snapshot_when_empty(const_cast<const ParentT&>(parent)); + _test_get_find_latest_snapshot_when_empty(const_cast<ParentT&>(parent)); } + template <class ParentT> - void _test_get_find_core_segment_by_id(ParentT&& parent) + void + test_get_find_entity_by_id(ParentT&& parent) + { + _test_get_find_entity_by_id(const_cast<const ParentT&>(parent)); + _test_get_find_entity_by_id(const_cast<ParentT&>(parent)); + } + + template <class ParentT> + void + _test_get_find_provider_segment_by_id(ParentT&& parent) { BOOST_TEST_CONTEXT("Parent: " << armem::print(parent)) { - BOOST_CHECK_EQUAL(parent.hasCoreSegment(provSegID.withCoreSegmentName("core segment")), true); - BOOST_CHECK_EQUAL(parent.hasCoreSegment(provSegID.withCoreSegmentName("other core segment")), false); + BOOST_CHECK_EQUAL(parent.hasProviderSegment( + provSegID.withProviderSegmentName("provider segment")), + true); + BOOST_CHECK_EQUAL(parent.hasProviderSegment( + provSegID.withProviderSegmentName("other provider segment")), + false); + + BOOST_CHECK_NE(parent.findProviderSegment( + provSegID.withProviderSegmentName("provider segment")), + nullptr); + BOOST_CHECK_EQUAL(parent.findProviderSegment( + provSegID.withProviderSegmentName("other provider segment")), + nullptr); + + BOOST_CHECK_NO_THROW(parent.getProviderSegment( + provSegID.withProviderSegmentName("provider segment"))); + BOOST_CHECK_THROW(parent.getProviderSegment( + provSegID.withProviderSegmentName("other provider segment")), + armem::error::MissingEntry); + } + } - BOOST_CHECK_NE(parent.findCoreSegment(provSegID.withCoreSegmentName("core segment")), nullptr); - BOOST_CHECK_EQUAL(parent.findCoreSegment(provSegID.withCoreSegmentName("other core segment")), nullptr); + template <class ParentT> + void + test_get_find_provider_segment_by_id(ParentT&& parent) + { + _test_get_find_provider_segment_by_id(const_cast<const ParentT&>(parent)); + _test_get_find_provider_segment_by_id(const_cast<ParentT&>(parent)); + } - BOOST_CHECK_NO_THROW(parent.getCoreSegment(provSegID.withCoreSegmentName("core segment"))); - BOOST_CHECK_THROW(parent.getCoreSegment(provSegID.withCoreSegmentName("other core segment")), armem::error::MissingEntry); + template <class ParentT> + void + _test_get_find_core_segment_by_id(ParentT&& parent) + { + BOOST_TEST_CONTEXT("Parent: " << armem::print(parent)) + { + BOOST_CHECK_EQUAL( + parent.hasCoreSegment(provSegID.withCoreSegmentName("core segment")), true); + BOOST_CHECK_EQUAL( + parent.hasCoreSegment(provSegID.withCoreSegmentName("other core segment")), + false); + + BOOST_CHECK_NE( + parent.findCoreSegment(provSegID.withCoreSegmentName("core segment")), nullptr); + BOOST_CHECK_EQUAL( + parent.findCoreSegment(provSegID.withCoreSegmentName("other core segment")), + nullptr); + + BOOST_CHECK_NO_THROW( + parent.getCoreSegment(provSegID.withCoreSegmentName("core segment"))); + BOOST_CHECK_THROW( + parent.getCoreSegment(provSegID.withCoreSegmentName("other core segment")), + armem::error::MissingEntry); } } + + template <class ParentT> + void + test_get_find_core_segment_by_id(ParentT&& parent) + { + _test_get_find_core_segment_by_id(const_cast<const ParentT&>(parent)); + _test_get_find_core_segment_by_id(const_cast<ParentT&>(parent)); + } }; -} +} // namespace ArMemGetFindTest BOOST_FIXTURE_TEST_SUITE(ArMemGetFindTest, Fixture) - - BOOST_AUTO_TEST_CASE(test_snapshot_get_find_instance_by_key) { BOOST_CHECK_EQUAL(snapshot.hasInstance(0), true); @@ -220,20 +371,20 @@ BOOST_AUTO_TEST_CASE(test_snapshot_get_find_instance_by_key) BOOST_CHECK_THROW(snapshot.getInstance(1), armem::error::MissingEntry); } - BOOST_AUTO_TEST_CASE(test_entity_get_find_snapshot_by_key) { BOOST_CHECK_EQUAL(entity.hasSnapshot(armem::Time(armem::Duration::MicroSeconds(1000))), true); BOOST_CHECK_EQUAL(entity.hasSnapshot(armem::Time(armem::Duration::MicroSeconds(2000))), false); BOOST_CHECK_NE(entity.findSnapshot(armem::Time(armem::Duration::MicroSeconds(1000))), nullptr); - BOOST_CHECK_EQUAL(entity.findSnapshot(armem::Time(armem::Duration::MicroSeconds(2000))), nullptr); + BOOST_CHECK_EQUAL(entity.findSnapshot(armem::Time(armem::Duration::MicroSeconds(2000))), + nullptr); BOOST_CHECK_NO_THROW(entity.getSnapshot(armem::Time(armem::Duration::MicroSeconds(1000)))); - BOOST_CHECK_THROW(entity.getSnapshot(armem::Time(armem::Duration::MicroSeconds(2000))), armem::error::MissingEntry); + BOOST_CHECK_THROW(entity.getSnapshot(armem::Time(armem::Duration::MicroSeconds(2000))), + armem::error::MissingEntry); } - BOOST_AUTO_TEST_CASE(test_provider_segment_get_find_entity_by_key) { BOOST_CHECK_EQUAL(provSeg.hasEntity("entity"), true); @@ -246,7 +397,6 @@ BOOST_AUTO_TEST_CASE(test_provider_segment_get_find_entity_by_key) BOOST_CHECK_THROW(provSeg.getEntity("other entity"), armem::error::MissingEntry); } - BOOST_AUTO_TEST_CASE(test_core_segment_get_find_provider_segment_by_key) { BOOST_CHECK_EQUAL(coreSeg.hasProviderSegment("provider segment"), true); @@ -256,10 +406,10 @@ BOOST_AUTO_TEST_CASE(test_core_segment_get_find_provider_segment_by_key) BOOST_CHECK_EQUAL(coreSeg.findProviderSegment("other provider segment"), nullptr); BOOST_CHECK_NO_THROW(coreSeg.getProviderSegment("provider segment")); - BOOST_CHECK_THROW(coreSeg.getProviderSegment("other provider segment"), armem::error::MissingEntry); + BOOST_CHECK_THROW(coreSeg.getProviderSegment("other provider segment"), + armem::error::MissingEntry); } - BOOST_AUTO_TEST_CASE(test_memory_get_find_core_segment_by_key) { BOOST_CHECK_EQUAL(memory.hasCoreSegment("core segment"), true); @@ -272,66 +422,119 @@ BOOST_AUTO_TEST_CASE(test_memory_get_find_core_segment_by_key) BOOST_CHECK_THROW(memory.getCoreSegment("other core segment"), armem::error::MissingEntry); } - - BOOST_AUTO_TEST_CASE(test_snapshot_get_find_instance_by_id) { test_get_find_instance_by_id(snapshot); } + BOOST_AUTO_TEST_CASE(test_entity_get_find_instance_by_id) { test_get_find_instance_by_id(entity); } + BOOST_AUTO_TEST_CASE(test_provider_segment_get_find_instance_by_id) { test_get_find_instance_by_id(provSeg); } + BOOST_AUTO_TEST_CASE(test_core_segment_get_find_instance_by_id) { test_get_find_instance_by_id(coreSeg); } + BOOST_AUTO_TEST_CASE(test_memory_get_find_instance_by_id) { test_get_find_instance_by_id(memory); } +BOOST_AUTO_TEST_CASE(test_entity_get_find_latest_instance) +{ + test_get_find_latest_instance(entity); + test_get_find_latest_instance_when_empty(entityEmpty); +} + +BOOST_AUTO_TEST_CASE(test_provider_segment_get_find_latest_instance) +{ + test_get_find_latest_instance(provSeg); + test_get_find_latest_instance_when_empty(provSegEmpty); +} + +BOOST_AUTO_TEST_CASE(test_core_segment_get_find_latest_instance) +{ + test_get_find_latest_instance(coreSeg); + test_get_find_latest_instance_when_empty(coreSegEmpty); +} + +BOOST_AUTO_TEST_CASE(test_memory_get_find_latest_instance) +{ + test_get_find_latest_instance(memory); + test_get_find_latest_instance_when_empty(memoryEmpty); +} BOOST_AUTO_TEST_CASE(test_entity_get_find_snapshot_by_id) { test_get_find_snapshot_by_id(entity); } + BOOST_AUTO_TEST_CASE(test_provider_segment_get_find_snapshot_by_id) { test_get_find_snapshot_by_id(provSeg); } + BOOST_AUTO_TEST_CASE(test_core_segment_get_find_snapshot_by_id) { test_get_find_snapshot_by_id(coreSeg); } + BOOST_AUTO_TEST_CASE(test_memory_get_find_snapshot_by_id) { test_get_find_snapshot_by_id(memory); } +BOOST_AUTO_TEST_CASE(test_entity_get_find_latest_snapshot) +{ + test_get_find_latest_snapshot(entity); + test_get_find_latest_snapshot_when_empty(entityEmpty); +} + +BOOST_AUTO_TEST_CASE(test_provider_segment_get_find_latest_snapshot) +{ + test_get_find_latest_snapshot(provSeg); + test_get_find_latest_snapshot_when_empty(provSegEmpty); +} + +BOOST_AUTO_TEST_CASE(test_core_segment_get_find_latest_snapshot) +{ + test_get_find_latest_snapshot(coreSeg); + test_get_find_latest_snapshot_when_empty(coreSegEmpty); +} + +BOOST_AUTO_TEST_CASE(test_memory_get_find_latest_snapshot) +{ + test_get_find_latest_snapshot(memory); + test_get_find_latest_snapshot_when_empty(memoryEmpty); +} BOOST_AUTO_TEST_CASE(test_provider_segment_get_find_entity_by_id) { test_get_find_entity_by_id(provSeg); } + BOOST_AUTO_TEST_CASE(test_core_segment_get_find_entity_by_id) { test_get_find_entity_by_id(coreSeg); } + BOOST_AUTO_TEST_CASE(test_memory_get_find_entity_by_id) { test_get_find_entity_by_id(memory); } - BOOST_AUTO_TEST_CASE(test_core_segment_get_find_provider_segment_by_id) { test_get_find_provider_segment_by_id(coreSeg); } + BOOST_AUTO_TEST_CASE(test_memory_get_find_provider_segment_by_id) { test_get_find_provider_segment_by_id(memory); @@ -342,6 +545,4 @@ BOOST_AUTO_TEST_CASE(test_memory_get_find_core_segment_by_id) test_get_find_core_segment_by_id(memory); } - - BOOST_AUTO_TEST_SUITE_END() -- GitLab