diff --git a/src/autotesting/AutowiringEnclosure.h b/src/autotesting/AutowiringEnclosure.h index 4a928830a..5d5cea75b 100644 --- a/src/autotesting/AutowiringEnclosure.h +++ b/src/autotesting/AutowiringEnclosure.h @@ -5,6 +5,7 @@ #include #include #include MEMORY_HEADER +#include THREAD_HEADER #ifndef GTEST_INCLUDE_GTEST_GTEST_H_ #error Please include your version of gtest.h before including the autowiring enclosure @@ -50,15 +51,25 @@ struct TestInfoProxy { }; namespace autowiring { - namespace testing { + namespace autotesting { struct hung { CoreContext& ctxt; }; - std::ostream& operator<<(std::ostream& os, const hung& lhs) { + inline std::ostream& operator<<(std::ostream& os, const hung& lhs) { autowiring::dbg::PrintRunnables(os, lhs.ctxt); return os; } + + template + inline bool WaitForUseCount(const T& type, long useCount, Duration duration) { + const auto limit = std::chrono::steady_clock::now() + duration; + do { + if (useCount == type.use_count()) return true; + std::this_thread::yield(); + } while (std::chrono::steady_clock::now() < limit); + return false; + } } } @@ -135,7 +146,7 @@ class AutowiringEnclosure: // Do not allow teardown to take more than 5 seconds. This is considered a "timely teardown" limit. // If it takes more than this amount of time to tear down, the test case itself should invoke SignalShutdown // and Wait itself with the extended teardown period specified. - ASSERT_TRUE(ctxt->Wait(std::chrono::seconds(5))) << "Test case took too long to tear down, unit tests running after this point are untrustworthy. Runnable dump:\n" << autowiring::testing::hung{ *ctxt }; + ASSERT_TRUE(ctxt->Wait(std::chrono::seconds(5))) << "Test case took too long to tear down, unit tests running after this point are untrustworthy. Runnable dump:\n" << autowiring::autotesting::hung{ *ctxt }; // Global context should return to quiescence: if (!allowGlobalReferences) @@ -151,7 +162,7 @@ class AutowiringEnclosure: } // No more references to this context except for the pointer we hold ourselves - ASSERT_TRUE(ctxt.unique()) << "Detected a dangling context reference after test termination, context may be leaking"; + ASSERT_TRUE(autowiring::autotesting::WaitForUseCount(ctxt, 1L, std::chrono::seconds(5))) << "Detected a dangling context reference after test termination, context may be leaking"; ctxt = {}; } }; diff --git a/src/autowiring/test/ContextCleanupTest.cpp b/src/autowiring/test/ContextCleanupTest.cpp index 56a08e3d1..ea35226f0 100644 --- a/src/autowiring/test/ContextCleanupTest.cpp +++ b/src/autowiring/test/ContextCleanupTest.cpp @@ -2,6 +2,7 @@ #include "stdafx.h" #include "TestFixtures/SimpleObject.hpp" #include "TestFixtures/SimpleThreaded.hpp" +#include "autotesting/AutowiringEnclosure.h" #include #include #include THREAD_HEADER @@ -19,7 +20,7 @@ TEST_F(ContextCleanupTest, ValidateTeardownOrder) { std::weak_ptr self; }; - + // Construct, the destroy std::make_shared(); } @@ -227,6 +228,5 @@ TEST_F(ContextCleanupTest, VerifyThreadShutdownInterleave) { ctxt->SignalShutdown(true); // At this point, the thread must have returned AND released its shared pointer to the enclosing context - ASSERT_EQ(initCount, ctxt.use_count()) << "Context thread persisted even after it should have fallen out of scope"; + ASSERT_TRUE(autowiring::autotesting::WaitForUseCount(ctxt, initCount, std::chrono::seconds(5))) << "Context thread persisted even after it should have fallen out of scope"; } - diff --git a/src/autowiring/test/ContextMapTest.cpp b/src/autowiring/test/ContextMapTest.cpp index 095d3ed05..0d180c330 100644 --- a/src/autowiring/test/ContextMapTest.cpp +++ b/src/autowiring/test/ContextMapTest.cpp @@ -2,6 +2,7 @@ #include "stdafx.h" #include "TestFixtures/ExitRaceThreaded.hpp" #include "TestFixtures/SimpleThreaded.hpp" +#include "autotesting/AutowiringEnclosure.h" #include #include #include @@ -76,11 +77,11 @@ TEST_F(ContextMapTest, VerifyWithThreads) { // Terminate whole context, wait for it to respond context->SignalShutdown(); context->Wait(); - ASSERT_EQ(1UL, context.use_count()) << "Context reference should have been unique after thread expiration"; + ASSERT_TRUE(autowiring::autotesting::WaitForUseCount(context, 1L, std::chrono::seconds(5))) << "Context reference should have been unique after thread expiration"; } // Release our threaded entity: - ASSERT_EQ(1UL, threaded.use_count()) << "Thread was holding a self-reference even after context termination has completed"; + ASSERT_TRUE(autowiring::autotesting::WaitForUseCount(threaded, 1L, std::chrono::seconds(5))) << "Thread was holding a self-reference even after context termination has completed"; threaded.reset(); ASSERT_TRUE(weakContext.expired()) << "Context still existed even after the last reference to it should have been gone"; diff --git a/src/autowiring/test/CoreThreadTest.cpp b/src/autowiring/test/CoreThreadTest.cpp index 2acc61077..9f2e24735 100644 --- a/src/autowiring/test/CoreThreadTest.cpp +++ b/src/autowiring/test/CoreThreadTest.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2012-2017 Leap Motion, Inc. All rights reserved. #include "stdafx.h" #include "TestFixtures/SimpleThreaded.hpp" +#include "autotesting/AutowiringEnclosure.h" #include #include #include @@ -570,7 +571,7 @@ TEST_F(CoreThreadTest, ContextWaitTimesOutInOnStop) { // Let BIOS back out now: bios->Continue(); ASSERT_TRUE(ctxt->Wait(std::chrono::seconds(5))) << "Context did not complete in a timely fashion"; - ASSERT_EQ(2UL, ctxt.use_count()) << "Entity held a context shared pointer after teardown has taken place"; + ASSERT_TRUE(autowiring::autotesting::WaitForUseCount(ctxt, 2L, std::chrono::seconds(5))) << "Entity held a context shared pointer after teardown has taken place"; } TEST_F(CoreThreadTest, SubContextHoldsParentContext) {