-
Notifications
You must be signed in to change notification settings - Fork 84
Fix flaky test #385
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Fix flaky test #385
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -263,50 +263,6 @@ ServiceDataStorage& GetServiceDataStorage(const memory::shared::ManagedMemoryRes | |
|
|
||
| } // namespace detail_proxy | ||
|
|
||
| class FindServiceGuard final | ||
| { | ||
| public: | ||
| // Suppress "AUTOSAR C++14 A15-5-3" rule findings. This rule states: "The std::terminate() function shall not be | ||
| // called implicitly". std::terminate() is implicitly called from 'find_service_handle_result.value()' in case | ||
| // find_service_handle_result doesn't have a value but as we check before with 'has_value()' so no way for throwing | ||
| // std::bad_optional_access which leds to std::terminate(). | ||
| // coverity[autosar_cpp14_a15_5_3_violation : FALSE] | ||
| FindServiceGuard(FindServiceHandler<HandleType> find_service_handler, | ||
| EnrichedInstanceIdentifier enriched_instance_identifier) | ||
| : service_availability_change_handle_{nullptr} | ||
| { | ||
| auto& service_discovery = impl::Runtime::getInstance().GetServiceDiscovery(); | ||
| const auto find_service_handle_result = service_discovery.StartFindService( | ||
| std::move(find_service_handler), std::move(enriched_instance_identifier)); | ||
| if (!find_service_handle_result.has_value()) | ||
| { | ||
| score::mw::log::LogFatal("lola") | ||
| << "StartFindService failed with error" << find_service_handle_result.error() << ". Terminating."; | ||
| std::terminate(); | ||
| } | ||
| service_availability_change_handle_ = std::make_unique<FindServiceHandle>(find_service_handle_result.value()); | ||
| } | ||
|
|
||
| ~FindServiceGuard() noexcept | ||
| { | ||
| auto& service_discovery = impl::Runtime::getInstance().GetServiceDiscovery(); | ||
| const auto stop_find_service_result = service_discovery.StopFindService(*service_availability_change_handle_); | ||
| if (!stop_find_service_result.has_value()) | ||
| { | ||
| score::mw::log::LogError("lola") | ||
| << "StopFindService failed with error" << stop_find_service_result.error() << ". Ignoring error."; | ||
| } | ||
| } | ||
|
|
||
| FindServiceGuard(const FindServiceGuard&) = delete; | ||
| FindServiceGuard& operator=(const FindServiceGuard&) = delete; | ||
| FindServiceGuard(FindServiceGuard&& other) = delete; | ||
| FindServiceGuard& operator=(FindServiceGuard&& other) = delete; | ||
|
|
||
| private: | ||
| std::unique_ptr<FindServiceHandle> service_availability_change_handle_; | ||
| }; | ||
|
|
||
| std::atomic<ProxyInstanceIdentifier::ProxyInstanceCounter> Proxy::current_proxy_instance_counter_{0U}; | ||
|
|
||
| ElementFqId Proxy::EventNameToElementFqIdConverter::Convert(const std::string_view event_name) const noexcept | ||
|
|
@@ -430,20 +386,20 @@ Proxy::Proxy(std::shared_ptr<memory::shared::ManagedMemoryResource> control, | |
| offered_state_machine_{}, | ||
| are_proxy_methods_setup_{false}, | ||
| filesystem_{filesystem}, | ||
| find_service_guard_{std::make_unique<FindServiceGuard>( | ||
| [this](ServiceHandleContainer<HandleType> service_handle_container, FindServiceHandle) { | ||
| // Suppress Autosar C++14 A8-5-3 states that auto variables shall not be initialized using braced | ||
| // initialization. This is a false positive, we don't use auto here | ||
| // coverity[autosar_cpp14_a8_5_3_violation : FALSE] | ||
| std::lock_guard lock{proxy_event_registration_mutex_}; | ||
| is_service_instance_available_ = !service_handle_container.empty(); | ||
| ServiceAvailabilityChangeHandler(is_service_instance_available_); | ||
| }, | ||
| EnrichedInstanceIdentifier{handle_})} | ||
| find_service_handle_{} | ||
| { | ||
| StartProxyAutoReconnect(); | ||
| } | ||
|
|
||
| Proxy::~Proxy() = default; | ||
| Proxy::~Proxy() | ||
| { | ||
| if (!prepare_unsubscribe_called_ || !finalize_unsubscribe_called_) | ||
| { | ||
| score::mw::log::LogFatal("lola") | ||
| << "Proxy destroyed without prior PrepareUnsubscribe + FinalizeUnsubscribe. Terminating."; | ||
| std::terminate(); | ||
| } | ||
| } | ||
|
|
||
| void Proxy::ServiceAvailabilityChangeHandler(const bool is_service_available) | ||
| { | ||
|
|
@@ -929,4 +885,61 @@ void Proxy::RegisterMethod(const UniqueMethodIdentifier method_id, ProxyMethod& | |
| SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(was_inserted, "Method IDs must be unique!"); | ||
| } | ||
|
|
||
| void Proxy::PrepareUnsubscribe() | ||
| { | ||
| StopProxyAutoReconnect(); | ||
| prepare_unsubscribe_called_ = true; | ||
| } | ||
|
|
||
| void Proxy::FinalizeUnsubscribe() | ||
| { | ||
| { | ||
| std::lock_guard lock{proxy_event_registration_mutex_}; | ||
| event_bindings_.clear(); | ||
| is_service_instance_available_ = false; | ||
| } | ||
| { | ||
| std::lock_guard lock{proxy_method_registration_mutex_}; | ||
| proxy_methods_.clear(); | ||
| } | ||
| finalize_unsubscribe_called_ = true; | ||
| } | ||
|
|
||
| void Proxy::StartProxyAutoReconnect() | ||
| { | ||
| auto& service_discovery = impl::Runtime::getInstance().GetServiceDiscovery(); | ||
| const auto find_service_handle_result = service_discovery.StartFindService( | ||
| [this](ServiceHandleContainer<HandleType> service_handle_container, FindServiceHandle) { | ||
| std::lock_guard lock{proxy_event_registration_mutex_}; | ||
| is_service_instance_available_ = !service_handle_container.empty(); | ||
| ServiceAvailabilityChangeHandler(is_service_instance_available_); | ||
| }, | ||
| EnrichedInstanceIdentifier{handle_}); | ||
| if (!find_service_handle_result.has_value()) | ||
| { | ||
| score::mw::log::LogFatal("lola") | ||
| << "StartFindService failed with error" << find_service_handle_result.error() << ". Terminating."; | ||
| std::terminate(); | ||
| } | ||
| find_service_handle_ = find_service_handle_result.value(); | ||
| } | ||
|
|
||
| void Proxy::StopProxyAutoReconnect() | ||
| { | ||
| if (!find_service_handle_.has_value()) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be an assertion? If the guard fails to be created then we terminate in StartProxyAutoReconnect. |
||
| { | ||
| return; | ||
| } | ||
| // StopFindService waits for any in-flight handler to finish; must run before we take | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't really understand the point of the second part of this comment. The handler is run in a separate thread so I wouldn't expect a deadlock, but anyway, we have no reason to lock proxy_event_registration_mutex_ here? |
||
| // proxy_event_registration_mutex_ since the handler also takes it. | ||
| auto& service_discovery = impl::Runtime::getInstance().GetServiceDiscovery(); | ||
| const auto stop_find_service_result = service_discovery.StopFindService(*find_service_handle_); | ||
| if (!stop_find_service_result.has_value()) | ||
| { | ||
| score::mw::log::LogError("lola") | ||
| << "StopFindService failed with error" << stop_find_service_result.error() << ". Ignoring error."; | ||
| } | ||
| find_service_handle_.reset(); | ||
| } | ||
|
|
||
| } // namespace score::mw::com::impl::lola | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -27,6 +27,7 @@ | |||||
| #include "score/mw/com/impl/configuration/lola_service_instance_id.h" | ||||||
| #include "score/mw/com/impl/configuration/lola_service_type_deployment.h" | ||||||
| #include "score/mw/com/impl/configuration/quality_type.h" | ||||||
| #include "score/mw/com/impl/find_service_handle.h" | ||||||
| #include "score/mw/com/impl/handle_type.h" | ||||||
| #include "score/mw/com/impl/proxy_binding.h" | ||||||
| #include "score/mw/com/impl/proxy_event_binding_base.h" | ||||||
|
|
@@ -62,7 +63,6 @@ namespace score::mw::com::impl::lola | |||||
| { | ||||||
|
|
||||||
| class IShmPathBuilder; | ||||||
| class FindServiceGuard; | ||||||
|
|
||||||
| namespace detail_proxy | ||||||
| { | ||||||
|
|
@@ -200,9 +200,15 @@ class Proxy : public ProxyBinding | |||||
|
|
||||||
| void RegisterMethod(const UniqueMethodIdentifier method_id, ProxyMethod& proxy_method) noexcept; | ||||||
|
|
||||||
| void PrepareUnsubscribe() override; | ||||||
| void FinalizeUnsubscribe() override; | ||||||
|
|
||||||
| private: | ||||||
| static std::atomic<ProxyInstanceIdentifier::ProxyInstanceCounter> current_proxy_instance_counter_; | ||||||
|
|
||||||
| void StartProxyAutoReconnect(); | ||||||
| void StopProxyAutoReconnect(); | ||||||
|
|
||||||
| void ServiceAvailabilityChangeHandler(const bool is_service_available); | ||||||
| void InitializeSharedMemoryForMethods( | ||||||
| memory::shared::ManagedMemoryResource& memory_resource, | ||||||
|
|
@@ -261,9 +267,13 @@ class Proxy : public ProxyBinding | |||||
|
|
||||||
| score::filesystem::Filesystem filesystem_; | ||||||
|
|
||||||
| // We make find_service_guard_ the last member variable since it registers a handler which accesses member variables | ||||||
| // of this class, so they should be initialised first. | ||||||
| std::unique_ptr<FindServiceGuard> find_service_guard_; | ||||||
| /// Handle returned by ServiceDiscovery::StartFindService. Held so we can call StopFindService later from | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| /// StopProxyAutoReconnect(). | ||||||
| std::optional<FindServiceHandle> find_service_handle_; | ||||||
|
|
||||||
| /// Set by PrepareUnsubscribe / FinalizeUnsubscribe respectively. ~Proxy terminates unless both were called. | ||||||
| bool prepare_unsubscribe_called_{false}; | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Initialise them in the constructor |
||||||
| bool finalize_unsubscribe_called_{false}; | ||||||
| }; | ||||||
|
|
||||||
| template <typename EventSampleType> | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -100,6 +100,16 @@ class LolaProxyEventCommonFixture : public ProxyMockedMemoryFixture | |
| UnregisterEventNotification(QualityType::kASIL_QM, kElementFqId, my_handler_no, kDummyPid)); | ||
| } | ||
|
|
||
| // ProxyEventCommon no longer Unsubscribes on destruction; release state-machine state explicitly. | ||
| void TearDown() override | ||
| { | ||
| if (proxy_event_ != nullptr) | ||
| { | ||
| proxy_event_->Unsubscribe(); | ||
| } | ||
| ProxyMockedMemoryFixture::TearDown(); | ||
| } | ||
|
|
||
| protected: | ||
| ::testing::MockFunction<void()> event_handler_{}; | ||
| std::unique_ptr<T> proxy_event_{nullptr}; | ||
|
|
@@ -280,7 +290,8 @@ TYPED_TEST(LolaProxyEventCommonFixture, DestroyingTheProxyEventWillUnregisterEve | |
| // Then the receive handler should not be unregistered | ||
| EXPECT_FALSE(handler_unregistered); | ||
|
|
||
| // and when the ProxyEvent is destroyed | ||
| // and when the ProxyEvent is unsubscribed and destroyed | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this really done when destroying the event? Or just when unsubscribing?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm, this test name does not fit well now,
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, it should be removed since it's testing the raii semantics which no longer exist. |
||
| this->proxy_event_->Unsubscribe(); | ||
| this->proxy_event_.reset(); | ||
|
|
||
| // Then the receive handler should be unregistered | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -358,6 +358,7 @@ auto Skeleton::PrepareOffer(SkeletonEventBindings& events, | |
| method_subscription_registration_guard_asil_b_.emplace(std::move(asil_b_registration_result).value()); | ||
| } | ||
|
|
||
| prepare_offer_called_ = true; | ||
| return {}; | ||
| } | ||
|
|
||
|
|
@@ -370,6 +371,8 @@ auto Skeleton::PrepareOffer(SkeletonEventBindings& events, | |
| auto Skeleton::PrepareStopOffer(std::optional<UnregisterShmObjectTraceCallback> unregister_shm_object_callback) noexcept | ||
| -> void | ||
| { | ||
| prepare_stop_offer_called_ = true; | ||
|
|
||
| if (unregister_shm_object_callback.has_value()) | ||
| { | ||
| // Suppress "AUTOSAR C++14 A15-4-2" rule finding. This rule states: "I a function is declared to be | ||
|
|
@@ -443,6 +446,16 @@ auto Skeleton::PrepareStopOffer(std::optional<UnregisterShmObjectTraceCallback> | |
| memory_manager_.Reset(); | ||
| } | ||
|
|
||
| Skeleton::~Skeleton() noexcept | ||
| { | ||
| if (prepare_offer_called_ && !prepare_stop_offer_called_) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmmmm, this starts to become a bit problematic IMO because we start adding logic here which is reliant on the logic of the layer above (i.e. SkeletonBase only calls PrepareStopOffer if the service was offered). But then on proxy side, unsubscribe is always called. We should discuss this. |
||
| { | ||
| score::mw::log::LogFatal("lola") | ||
| << "Skeleton was offered but destroyed without prior PrepareStopOffer. Terminating."; | ||
| std::terminate(); | ||
| } | ||
| } | ||
|
|
||
| QualityType Skeleton::GetInstanceQualityType() const | ||
| { | ||
| return quality_type_; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This commit should be squashed with the previous one since tests should be merged alongside the changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
both commits would be merged at once anyway since this is one PR,
but if you are fine with all the things in one commit then i will do it like this,
usually ppl ask for separate commit for tests in the past. since you are reviewing it i will squash as you asked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Commits should be split up into small but complete parts. E.g. in this PR, you probably should have had one commit which adds PrepareUnsubscribe / FinalizeUnsubscribe and a second commit which adds the checks to ~Proxy() which modifies a lot of tests. (and then another commit to add checks to ~Skeleton etc).
Splitting the pr up serves a few purposes: 1) It makes it easier to review and also if we see that the pr is too big, makes it easier to split it up. 2) In the future, it makes it easier to understand a full change by checking the commit without having to try to find the pr that merged the change (this is also why these commits like "Address review comments" are not good). 3) If we ever want to revert a change, then you can just revert a single commit. If the change and tests are in multiple commits, you would have to revert all of them (and if you don't realise there are multiple commits then you might end up manually reverting all the tests). E.G. It's possible in the future that we prefer not to have the check in ~Proxy(). With all the changes together as they are now, we can't revert the change at all because we still need the unsubscribe change.