// Copyright (c) 2019 by Robert Bosch GmbH. All rights reserved. // Copyright (c) 2021 by Apex.AI Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // SPDX-License-Identifier: Apache-2.0 #include "iceoryx_hoofs/cxx/expected.hpp" #include "iceoryx_hoofs/error_handling/error_handling.hpp" #include "iceoryx_hoofs/internal/units/duration.hpp" #include "iceoryx_hoofs/posix_wrapper/timer.hpp" #include "iceoryx_hoofs/testing/test.hpp" #include "iceoryx_hoofs/testing/timing_test.hpp" #include #include #include namespace { using namespace ::testing; using namespace iox::units; using namespace iox::units::duration_literals; using Timer = iox::posix::Timer; using TimerError = iox::posix::TimerError; class Timer_test : public Test { public: virtual void SetUp() { numberOfCalls = 0; } virtual void TearDown() { } Duration second{1_s}; std::atomic numberOfCalls{0}; static const Duration TIMEOUT; }; class TimerStopWatch_test : public Test { public: static const Duration TIMEOUT; }; const Duration TimerStopWatch_test::TIMEOUT{10_ms}; const Duration Timer_test::TIMEOUT{TimerStopWatch_test::TIMEOUT}; TEST_F(TimerStopWatch_test, DurationOfZeroCausesError) { ::testing::Test::RecordProperty("TEST_ID", "61067a67-7132-44e2-a99c-03ddb6ce963d"); Timer sut(0_s); EXPECT_THAT(sut.hasError(), Eq(true)); EXPECT_THAT(sut.getError(), Eq(TimerError::TIMEOUT_IS_ZERO)); } TEST_F(Timer_test, ZeroTimeoutIsNotAllowed) { ::testing::Test::RecordProperty("TEST_ID", "e93d95af-1604-4652-a3fd-9602f47f7d6f"); Timer sut(0_s, [] {}); EXPECT_THAT(sut.hasError(), Eq(true)); EXPECT_THAT(sut.getError(), Eq(iox::posix::TimerError::TIMEOUT_IS_ZERO)); } TIMING_TEST_F(Timer_test, CallbackNotExecutedWhenNotStarted, Repeat(5), [&] { bool callbackExecuted{false}; Timer sut(TIMEOUT, [&] { callbackExecuted = true; }); std::this_thread::sleep_for(std::chrono::milliseconds(4 * TIMEOUT.toMilliseconds() / 3)); TIMING_TEST_EXPECT_ALWAYS_FALSE(callbackExecuted); }); TIMING_TEST_F(Timer_test, CallbackExecutedOnceAfterStart, Repeat(5), [&] { std::atomic_int counter{0}; Timer sut(1_ns, [&] { counter++; }); ASSERT_FALSE(sut.start(Timer::RunMode::ONCE, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(10)); TIMING_TEST_EXPECT_TRUE(counter.load() == 1); }); TIMING_TEST_F(Timer_test, CallbackExecutedPeriodicallyAfterStart, Repeat(5), [&] { std::atomic_int counter{0}; Timer sut(TIMEOUT, [&] { counter++; }); ASSERT_FALSE(sut.start(Timer::RunMode::PERIODIC, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT.toMilliseconds() * 10)); auto finalCount = counter.load(); TIMING_TEST_EXPECT_TRUE(6 <= finalCount && finalCount <= 11); }); TIMING_TEST_F(Timer_test, PeriodicCallbackNotExecutedPrematurely, Repeat(5), [&] { std::atomic_int counter{0}; Timer sut(TIMEOUT, [&] { counter++; }); ASSERT_FALSE(sut.start(Timer::RunMode::PERIODIC, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(2 * TIMEOUT.toMilliseconds() / 3)); TIMING_TEST_EXPECT_TRUE(counter.load() == 0); }); TIMING_TEST_F(Timer_test, OneTimeCallbackNotExecutedPrematurely, Repeat(5), [&] { std::atomic_int counter{0}; Timer sut(TIMEOUT, [&] { counter++; }); ASSERT_FALSE(sut.start(Timer::RunMode::ONCE, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(2 * TIMEOUT.toMilliseconds() / 3)); TIMING_TEST_EXPECT_TRUE(counter.load() == 0); }); TEST_F(Timer_test, StartFailsWhenNoCallbackIsSet) { ::testing::Test::RecordProperty("TEST_ID", "a0029e9c-12e4-4bf6-a070-9c9afa5089cb"); Timer sut(1_ms); auto call = sut.start(Timer::RunMode::ONCE, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT); ASSERT_THAT(call.has_error(), Eq(true)); EXPECT_THAT(call.get_error(), Eq(TimerError::TIMER_NOT_INITIALIZED)); } TIMING_TEST_F(Timer_test, StartRunModeOnceIsStoppedAfterStop, Repeat(5), [&] { std::atomic_int counter{0}; Timer sut(TIMEOUT, [&] { counter++; }); ASSERT_FALSE(sut.start(Timer::RunMode::ONCE, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); ASSERT_FALSE(sut.stop().has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(4 * TIMEOUT.toMilliseconds() / 3)); TIMING_TEST_EXPECT_TRUE(counter.load() == 0); }); TIMING_TEST_F(Timer_test, StartRunPeriodicOnceIsStoppedAfterStop, Repeat(5), [&] { std::atomic_int counter{0}; Timer sut(TIMEOUT, [&] { counter++; }); ASSERT_FALSE(sut.start(Timer::RunMode::PERIODIC, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); ASSERT_FALSE(sut.stop().has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(4 * TIMEOUT.toMilliseconds() / 3)); TIMING_TEST_EXPECT_TRUE(counter.load() == 0); }); TIMING_TEST_F(Timer_test, StartRunPeriodicOnceIsStoppedInTheMiddleAfterStop, Repeat(5), [&] { std::atomic_int counter{0}; Timer sut(TIMEOUT, [&] { counter++; }); ASSERT_FALSE(sut.start(Timer::RunMode::PERIODIC, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(4 * TIMEOUT.toMilliseconds() / 3)); ASSERT_FALSE(sut.stop().has_error()); auto previousCount = counter.load(); std::this_thread::sleep_for(std::chrono::milliseconds(4 * TIMEOUT.toMilliseconds() / 3)); TIMING_TEST_EXPECT_TRUE(previousCount == counter.load()); }); TEST_F(Timer_test, StopFailsWhenNoCallbackIsSet) { ::testing::Test::RecordProperty("TEST_ID", "e1655ab0-7bf5-47bc-9991-8cd5ce3473c4"); Timer sut(1_ms); auto call = sut.stop(); ASSERT_THAT(call.has_error(), Eq(true)); EXPECT_THAT(call.get_error(), Eq(TimerError::TIMER_NOT_INITIALIZED)); } TIMING_TEST_F(Timer_test, RestartWithDifferentTiming, Repeat(5), [&] { std::atomic_int counter{0}; Timer sut(TIMEOUT * 10, [&] { counter++; }); ASSERT_FALSE(sut.start(Timer::RunMode::PERIODIC, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(20 * TIMEOUT.toMilliseconds())); ASSERT_FALSE(sut.restart(TIMEOUT, Timer::RunMode::PERIODIC, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); counter = 0; std::this_thread::sleep_for(std::chrono::milliseconds(10 * TIMEOUT.toMilliseconds())); auto finalCount = counter.load(); TIMING_TEST_EXPECT_TRUE(6 <= finalCount && finalCount <= 13); }); TIMING_TEST_F(Timer_test, RestartWithDifferentRunMode, Repeat(5), [&] { std::atomic_int counter{0}; Timer sut(TIMEOUT, [&] { counter++; }); ASSERT_FALSE(sut.start(Timer::RunMode::PERIODIC, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(4 * TIMEOUT.toMilliseconds() / 3)); ASSERT_FALSE(sut.restart(TIMEOUT, Timer::RunMode::ONCE, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); counter = 0; std::this_thread::sleep_for(std::chrono::milliseconds(2 * TIMEOUT.toMilliseconds() / 3)); TIMING_TEST_EXPECT_TRUE(counter.load() == 0); std::this_thread::sleep_for(std::chrono::milliseconds(2 * TIMEOUT.toMilliseconds() / 3)); TIMING_TEST_EXPECT_TRUE(counter.load() == 1); std::this_thread::sleep_for(std::chrono::milliseconds(2 * TIMEOUT.toMilliseconds() / 3)); TIMING_TEST_EXPECT_TRUE(counter.load() == 1); }); TIMING_TEST_F(Timer_test, RestartWithDifferentTimingAndRunMode, Repeat(5), [&] { std::atomic_int counter{0}; Timer sut(TIMEOUT * 2, [&] { counter++; }); ASSERT_FALSE(sut.start(Timer::RunMode::ONCE, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(5 * TIMEOUT.toMilliseconds())); counter = 0; ASSERT_FALSE(sut.restart(TIMEOUT, Timer::RunMode::PERIODIC, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(10 * TIMEOUT.toMilliseconds())); auto finalCount = counter.load(); TIMING_TEST_EXPECT_TRUE(6 <= finalCount && finalCount <= 13); }); TEST_F(Timer_test, RestartWithEmptyCallbackFails) { ::testing::Test::RecordProperty("TEST_ID", "146acdfc-3d1c-44e8-88fd-6a476d657541"); Timer sut(1_ms); auto call = sut.restart(1_s, Timer::RunMode::ONCE, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT); ASSERT_THAT(call.has_error(), Eq(true)); EXPECT_THAT(call.get_error(), Eq(TimerError::TIMER_NOT_INITIALIZED)); } TEST_F(Timer_test, RestartWithTimeoutOfZeroFails) { ::testing::Test::RecordProperty("TEST_ID", "3ecb4925-9b15-4eca-b3eb-6d325c336e46"); Timer sut(1_ms, [] {}); auto call = sut.restart(0_s, Timer::RunMode::ONCE, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT); ASSERT_THAT(call.has_error(), Eq(true)); EXPECT_THAT(call.get_error(), Eq(TimerError::TIMEOUT_IS_ZERO)); } TEST_F(Timer_test, TimeUntilExpirationFailsWithoutCallback) { ::testing::Test::RecordProperty("TEST_ID", "8bdfd766-e223-4da2-9e3c-85de02534e86"); Timer sut(1_ms); auto call = sut.timeUntilExpiration(); ASSERT_THAT(call.has_error(), Eq(true)); EXPECT_THAT(call.get_error(), Eq(TimerError::TIMER_NOT_INITIALIZED)); } TIMING_TEST_F(Timer_test, TimeUntilExpirationWithCallback, Repeat(5), [&] { Timer sut(TIMEOUT, [] {}); ASSERT_FALSE(sut.start(Timer::RunMode::PERIODIC, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); auto timeUntilExpiration = sut.timeUntilExpiration().value().toMilliseconds(); TIMING_TEST_EXPECT_TRUE(timeUntilExpiration > 2 * TIMEOUT.toMilliseconds() / 3); std::this_thread::sleep_for(std::chrono::milliseconds(2 * TIMEOUT.toMilliseconds() / 3)); timeUntilExpiration = sut.timeUntilExpiration().value().toMilliseconds(); TIMING_TEST_EXPECT_TRUE(1 <= timeUntilExpiration && timeUntilExpiration <= TIMEOUT.toMilliseconds() / 3); }); TIMING_TEST_F(Timer_test, TimeUntilExpirationZeroAfterCallbackOnceCalled, Repeat(5), [&] { Timer sut(TIMEOUT, [] {}); ASSERT_FALSE(sut.start(Timer::RunMode::ONCE, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(10 * TIMEOUT.toMilliseconds())); auto timeUntilExpiration = sut.timeUntilExpiration().value().toMilliseconds(); TIMING_TEST_EXPECT_TRUE(timeUntilExpiration == 0); }); TIMING_TEST_F(Timer_test, StoppingIsNonBlocking, Repeat(5), [&] { Timer sut(1_ns, [] { std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT.toMilliseconds() * 10)); }); ASSERT_FALSE(sut.start(Timer::RunMode::ONCE, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(1)); auto startTime = std::chrono::system_clock::now(); ASSERT_FALSE(sut.stop().has_error()); auto endTime = std::chrono::system_clock::now(); auto elapsedTime = std::chrono::duration_cast(endTime - startTime).count(); TIMING_TEST_EXPECT_TRUE(elapsedTime < 10); }); TIMING_TEST_F(Timer_test, MultipleTimersRunningContinuously, Repeat(5), [&] { struct TimeValPair { TimeValPair() : timer(TIMEOUT, [&] { this->value++; }) { } int value{0}; Timer timer; }; std::vector sutList(4); for (auto& sut : sutList) { ASSERT_FALSE(sut.timer.start(Timer::RunMode::PERIODIC, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); } constexpr int64_t REPETITIONS = 10; std::this_thread::sleep_for(std::chrono::milliseconds(REPETITIONS * TIMEOUT.toMilliseconds())); for (auto& sut : sutList) { ASSERT_FALSE(sut.timer.stop().has_error()); } std::this_thread::sleep_for(std::chrono::milliseconds(REPETITIONS * TIMEOUT.toMilliseconds())); for (auto& sut : sutList) { TIMING_TEST_EXPECT_TRUE(REPETITIONS / 2 <= sut.value && sut.value <= 3 * REPETITIONS / 2); } }); TIMING_TEST_F(Timer_test, MultipleTimersRunningOnce, Repeat(5), [&] { struct TimeValPair { TimeValPair() : timer(TIMEOUT, [&] { this->value++; }) { } int value{0}; Timer timer; }; std::vector sutList(4); for (auto& sut : sutList) { ASSERT_FALSE(sut.timer.start(Timer::RunMode::ONCE, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); } std::this_thread::sleep_for(std::chrono::milliseconds(10 * TIMEOUT.toMilliseconds())); for (auto& sut : sutList) { TIMING_TEST_EXPECT_TRUE(sut.value == 1); } }); TIMING_TEST_F(Timer_test, DestructorIsBlocking, Repeat(5), [&] { std::chrono::time_point startTime; { Timer sut(1_ns, [] { std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT.toMilliseconds() * 10)); }); ASSERT_FALSE(sut.start(Timer::RunMode::ONCE, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(10)); startTime = std::chrono::system_clock::now(); } auto endTime = std::chrono::system_clock::now(); auto elapsedTime = std::chrono::duration_cast(endTime - startTime).count(); TIMING_TEST_EXPECT_TRUE(10 <= elapsedTime); }); TIMING_TEST_F(Timer_test, StartStopAndStartAgainIsNonBlocking, Repeat(5), [&] { Timer sut(1_ns, [] { std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT.toMilliseconds() * 10)); }); ASSERT_FALSE(sut.start(Timer::RunMode::ONCE, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(1)); auto startTime = std::chrono::system_clock::now(); ASSERT_FALSE(sut.stop().has_error()); ASSERT_FALSE(sut.start(Timer::RunMode::PERIODIC, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); auto endTime = std::chrono::system_clock::now(); auto elapsedTime = std::chrono::duration_cast(endTime - startTime).count(); TIMING_TEST_EXPECT_TRUE(elapsedTime <= 1); }); TEST_F(Timer_test, GetOverrunsFailsWithNoCallback) { ::testing::Test::RecordProperty("TEST_ID", "5b8ee9db-9394-459a-b31d-64cd6d57dae8"); Timer sut(1_ms); auto call = sut.getOverruns(); ASSERT_THAT(call.has_error(), Eq(true)); EXPECT_THAT(call.get_error(), Eq(TimerError::TIMER_NOT_INITIALIZED)); } TIMING_TEST_F(Timer_test, CatchUpPolicySkipToNextBeatContinuesWhenCallbackIsLongerThenTriggerTime, Repeat(5), [&] { std::atomic_bool hasTerminated{false}; auto errorHandlerGuard = iox::ErrorHandler::setTemporaryErrorHandler( [&](const iox::Error, const std::function, const iox::ErrorLevel) { hasTerminated = true; }); Timer sut(TIMEOUT, [] { std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT.toMilliseconds() * 10)); }); ASSERT_FALSE(sut.start(Timer::RunMode::PERIODIC, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT.toMilliseconds() * 10)); TIMING_TEST_EXPECT_FALSE(hasTerminated); }); TIMING_TEST_F(Timer_test, CatchUpPolicyImmediateContinuesWhenCallbackIsLongerThenTriggerTime, Repeat(5), [&] { std::atomic_bool hasTerminated{false}; auto errorHandlerGuard = iox::ErrorHandler::setTemporaryErrorHandler( [&](const iox::Error, const std::function, const iox::ErrorLevel) { hasTerminated = true; }); Timer sut(TIMEOUT, [] { std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT.toMilliseconds() * 10)); }); ASSERT_FALSE(sut.start(Timer::RunMode::PERIODIC, Timer::CatchUpPolicy::IMMEDIATE).has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT.toMilliseconds() * 10)); TIMING_TEST_EXPECT_FALSE(hasTerminated); }); TIMING_TEST_F(Timer_test, CatchUpPolicyTerminateTerminatesWhenCallbackIsLongerThenTriggerTime, Repeat(5), [&] { std::atomic_bool hasTerminated{false}; auto errorHandlerGuard = iox::ErrorHandler::setTemporaryErrorHandler( [&](const iox::Error, const std::function, const iox::ErrorLevel) { hasTerminated = true; }); Timer sut(TIMEOUT, [] { std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT.toMilliseconds() * 10)); }); ASSERT_FALSE(sut.start(Timer::RunMode::PERIODIC, Timer::CatchUpPolicy::TERMINATE).has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT.toMilliseconds() * 10)); TIMING_TEST_EXPECT_TRUE(hasTerminated); }); TIMING_TEST_F(Timer_test, CatchUpPolicyChangeToTerminateChangesBehaviorToTerminate, Repeat(5), [&] { std::atomic_bool hasTerminated{false}; auto errorHandlerGuard = iox::ErrorHandler::setTemporaryErrorHandler( [&](const iox::Error, const std::function, const iox::ErrorLevel) { hasTerminated = true; }); Timer sut(TIMEOUT, [] { std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT.toMilliseconds() * 10)); }); ASSERT_FALSE(sut.start(Timer::RunMode::PERIODIC, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT.toMilliseconds() * 10)); ASSERT_FALSE(sut.restart(TIMEOUT, Timer::RunMode::PERIODIC, Timer::CatchUpPolicy::TERMINATE).has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT.toMilliseconds() * 10)); TIMING_TEST_EXPECT_TRUE(hasTerminated); }); TIMING_TEST_F(Timer_test, CatchUpPolicySkipToNextBeatSkipsCallbackWhenStillRunning, Repeat(5), [&] { std::atomic_int counter{0}; Timer sut(TIMEOUT, [&] { ++counter; // wait slightly longer then the timeout so that the effect is better measurable std::this_thread::sleep_for(std::chrono::microseconds(TIMEOUT.toMilliseconds() * 1100)); }); ASSERT_FALSE(sut.start(Timer::RunMode::PERIODIC, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT.toMilliseconds() * 100)); // every second callback is skipped since the runtime is slightly longer therefore // the counter must be in that range TIMING_TEST_EXPECT_TRUE(40 <= counter && counter <= 70); }); TIMING_TEST_F(Timer_test, CatchUpPolicyImmediateCallsCallbackImmediatelyAfterFinishing, Repeat(5), [&] { std::atomic_int counter{0}; Timer sut(TIMEOUT, [&] { ++counter; // wait slightly longer then the timeout so that the effect is better measurable std::this_thread::sleep_for(std::chrono::microseconds(TIMEOUT.toMilliseconds() * 1100)); }); ASSERT_FALSE(sut.start(Timer::RunMode::PERIODIC, Timer::CatchUpPolicy::IMMEDIATE).has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT.toMilliseconds() * 100)); // the asap timer should in theory call the callback 90 times since it is calling it right // after the last one finished and one callback takes 1.1 ms and we run for 100ms. TIMING_TEST_EXPECT_TRUE(70 < counter && counter <= 100); }); TIMING_TEST_F(Timer_test, CatchUpPolicySkipToNextBeatCallsLessCallbacksThanASAPTimer, Repeat(5), [&] { std::atomic_int counter{0}; Timer sut(TIMEOUT, [&] { ++counter; // wait slightly longer then the timeout so that the effect is better measurable std::this_thread::sleep_for(std::chrono::microseconds(TIMEOUT.toMilliseconds() * 1100)); }); ASSERT_FALSE(sut.start(Timer::RunMode::PERIODIC, Timer::CatchUpPolicy::SKIP_TO_NEXT_BEAT).has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT.toMilliseconds() * 100)); int softTimerCounter = counter.load(); ASSERT_FALSE(sut.stop().has_error()); counter.store(0); ASSERT_FALSE(sut.start(Timer::RunMode::PERIODIC, Timer::CatchUpPolicy::IMMEDIATE).has_error()); std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT.toMilliseconds() * 100)); int asapTimerCounter = counter.load(); ASSERT_FALSE(sut.stop().has_error()); TIMING_TEST_EXPECT_TRUE(softTimerCounter < asapTimerCounter); }); /// Unit tests which segfaults (issue #243). If the segfault is fixed this unit test has /// to be adjusted but for the moment it seems that it causes the segfault /// reliable. TEST_F(Timer_test, DISABLED_SelfTriggeringTimerWorksAndDoesNotCauseSegFault) { ::testing::Test::RecordProperty("TEST_ID", "9ac73c73-44f9-46c1-81d8-51f1dd2a203e"); Duration selfTriggerTimeout = 1_ns; int repetitions = 100; std::atomic_int counter{0}; { Timer sut{selfTriggerTimeout, [&] { /// this timing is set to provoke the segfault. if the timing is /// decreased the segfault is more unlikely to occure but with a /// value of 100 ms it always happens. see issue #243 std::this_thread::sleep_for(std::chrono::milliseconds(100)); if (counter < repetitions) { EXPECT_FALSE( sut.restart(selfTriggerTimeout, Timer::RunMode::ONCE, Timer::CatchUpPolicy::IMMEDIATE) .has_error()); } ++counter; }}; ASSERT_FALSE(sut.start(Timer::RunMode::ONCE, Timer::CatchUpPolicy::IMMEDIATE).has_error()); /// this time seems to be sufficient to cause the segfault std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } } } // namespace