From e4f93ee5346421b5fcf873a0509cf44151120055 Mon Sep 17 00:00:00 2001 From: Gerald Squelart Date: Thu, 2 Jun 2016 00:53:35 +0200 Subject: [PATCH] Bug 1271593 - Unit test for NS_NewRunnableFunction - r=froydnj Tests to verify that the number of copies and moves are as expected. Also check that the runnable is fully self-contained and can be used after the initial function objects have been destroyed or moved-from. MozReview-Commit-ID: ArwIG9BEhDX --HG-- extra : rebase_source : b2ee07294fcff17b76da468ddbaeb2b62d600536 --- xpcom/tests/TestThreadUtils.cpp | 219 +++++++++++++++++++++++++++++++- 1 file changed, 217 insertions(+), 2 deletions(-) diff --git a/xpcom/tests/TestThreadUtils.cpp b/xpcom/tests/TestThreadUtils.cpp index 521933827555..e94ef946f165 100644 --- a/xpcom/tests/TestThreadUtils.cpp +++ b/xpcom/tests/TestThreadUtils.cpp @@ -116,8 +116,225 @@ public: NS_IMPL_ISUPPORTS0(nsBar) +struct TestCopyWithNoMove +{ + explicit TestCopyWithNoMove(int* aCopyCounter) : mCopyCounter(aCopyCounter) {} + TestCopyWithNoMove(const TestCopyWithNoMove& a) : mCopyCounter(a.mCopyCounter) { ++mCopyCounter; }; + // No 'move' declaration, allows passing object by rvalue copy. + // Destructor nulls member variable... + ~TestCopyWithNoMove() { mCopyCounter = nullptr; } + // ... so we can check that the object is called when still alive. + void operator()() { MOZ_ASSERT(mCopyCounter); } + int* mCopyCounter; +}; +struct TestCopyWithDeletedMove +{ + explicit TestCopyWithDeletedMove(int* aCopyCounter) : mCopyCounter(aCopyCounter) {} + TestCopyWithDeletedMove(const TestCopyWithDeletedMove& a) : mCopyCounter(a.mCopyCounter) { ++mCopyCounter; }; + // Deleted move prevents passing by rvalue (even if copy would work) + TestCopyWithDeletedMove(TestCopyWithDeletedMove&&) = delete; + ~TestCopyWithDeletedMove() { mCopyCounter = nullptr; } + void operator()() { MOZ_ASSERT(mCopyCounter); } + int* mCopyCounter; +}; +struct TestMove +{ + explicit TestMove(int* aMoveCounter) : mMoveCounter(aMoveCounter) {} + TestMove(const TestMove&) = delete; + TestMove(TestMove&& a) : mMoveCounter(a.mMoveCounter) { a.mMoveCounter = nullptr; ++mMoveCounter; } + ~TestMove() { mMoveCounter = nullptr; } + void operator()() { MOZ_ASSERT(mMoveCounter); } + int* mMoveCounter; +}; +struct TestCopyMove +{ + TestCopyMove(int* aCopyCounter, int* aMoveCounter) : mCopyCounter(aCopyCounter), mMoveCounter(aMoveCounter) {} + TestCopyMove(const TestCopyMove& a) : mCopyCounter(a.mCopyCounter), mMoveCounter(a.mMoveCounter) { ++mCopyCounter; }; + TestCopyMove(TestCopyMove&& a) : mCopyCounter(a.mCopyCounter), mMoveCounter(a.mMoveCounter) { a.mMoveCounter = nullptr; ++mMoveCounter; } + ~TestCopyMove() { mCopyCounter = nullptr; mMoveCounter = nullptr; } + void operator()() { MOZ_ASSERT(mCopyCounter); MOZ_ASSERT(mMoveCounter); } + int* mCopyCounter; + int* mMoveCounter; +}; + +static int Expect(const char* aContext, int aCounter, int aMaxExpected) +{ + if (aCounter > aMaxExpected) { + fail("%s: expected %d max, got %d", aContext, aMaxExpected, aCounter); + return 1; + } + passed("%s: got %d <= %d as expected", aContext, aCounter, aMaxExpected); + return 0; +} + +int TestNS_NewRunnableFunction() +{ + int result = 0; + + // Test NS_NewRunnableFunction with copyable-only function object. + { + int copyCounter = 0; + { + nsCOMPtr trackedRunnable; + { + TestCopyWithNoMove tracker(©Counter); + trackedRunnable = NS_NewRunnableFunction(tracker); + // Original 'tracker' is destroyed here. + } + // Verify that the runnable contains a non-destroyed function object. + trackedRunnable->Run(); + } + result |= Expect("NS_NewRunnableFunction with copyable-only (and no move) function, copies", + copyCounter, 1); + } + { + int copyCounter = 0; + { + nsCOMPtr trackedRunnable; + { + // Passing as rvalue, but using copy. + // (TestCopyWithDeletedMove wouldn't allow this.) + trackedRunnable = NS_NewRunnableFunction(TestCopyWithNoMove(©Counter)); + } + trackedRunnable->Run(); + } + result |= Expect("NS_NewRunnableFunction with copyable-only (and no move) function rvalue, copies", + copyCounter, 1); + } + { + int copyCounter = 0; + { + nsCOMPtr trackedRunnable; + { + TestCopyWithDeletedMove tracker(©Counter); + trackedRunnable = NS_NewRunnableFunction(tracker); + } + trackedRunnable->Run(); + } + result |= Expect("NS_NewRunnableFunction with copyable-only (and deleted move) function, copies", + copyCounter, 1); + } + + // Test NS_NewRunnableFunction with movable-only function object. + { + int moveCounter = 0; + { + nsCOMPtr trackedRunnable; + { + TestMove tracker(&moveCounter); + trackedRunnable = NS_NewRunnableFunction(Move(tracker)); + } + trackedRunnable->Run(); + } + result |= Expect("NS_NewRunnableFunction with movable-only function, moves", + moveCounter, 1); + } + { + int moveCounter = 0; + { + nsCOMPtr trackedRunnable; + { + trackedRunnable = NS_NewRunnableFunction(TestMove(&moveCounter)); + } + trackedRunnable->Run(); + } + result |= Expect("NS_NewRunnableFunction with movable-only function rvalue, moves", + moveCounter, 1); + } + + // Test NS_NewRunnableFunction with copyable&movable function object. + { + int copyCounter = 0; + int moveCounter = 0; + { + nsCOMPtr trackedRunnable; + { + TestCopyMove tracker(©Counter, &moveCounter); + trackedRunnable = NS_NewRunnableFunction(Move(tracker)); + } + trackedRunnable->Run(); + } + result |= Expect("NS_NewRunnableFunction with copyable&movable function, copies", + copyCounter, 0); + result |= Expect("NS_NewRunnableFunction with copyable&movable function, moves", + moveCounter, 1); + } + { + int copyCounter = 0; + int moveCounter = 0; + { + nsCOMPtr trackedRunnable; + { + trackedRunnable = + NS_NewRunnableFunction(TestCopyMove(©Counter, &moveCounter)); + } + trackedRunnable->Run(); + } + result |= Expect("NS_NewRunnableFunction with copyable&movable function rvalue, copies", + copyCounter, 0); + result |= Expect("NS_NewRunnableFunction with copyable&movable function rvalue, moves", + moveCounter, 1); + } + + // Test NS_NewRunnableFunction with copyable-only lambda capture. + { + int copyCounter = 0; + { + nsCOMPtr trackedRunnable; + { + TestCopyWithNoMove tracker(©Counter); + // Expect 2 copies (here -> local lambda -> runnable lambda). + trackedRunnable = NS_NewRunnableFunction([tracker]() mutable { tracker(); }); + } + trackedRunnable->Run(); + } + result |= Expect("NS_NewRunnableFunction with copyable-only (and no move) capture, copies", + copyCounter, 2); + } + { + int copyCounter = 0; + { + nsCOMPtr trackedRunnable; + { + TestCopyWithDeletedMove tracker(©Counter); + // Expect 2 copies (here -> local lambda -> runnable lambda). + trackedRunnable = NS_NewRunnableFunction([tracker]() mutable { tracker(); }); + } + trackedRunnable->Run(); + } + result |= Expect("NS_NewRunnableFunction with copyable-only (and deleted move) capture, copies", + copyCounter, 2); + } + + // Note: Not possible to use move-only captures. + // (Until we can use C++14 generalized lambda captures) + + // Test NS_NewRunnableFunction with copyable&movable lambda capture. + { + int copyCounter = 0; + int moveCounter = 0; + { + nsCOMPtr trackedRunnable; + { + TestCopyMove tracker(©Counter, &moveCounter); + trackedRunnable = NS_NewRunnableFunction([tracker]() mutable { tracker(); }); + // Expect 1 copy (here -> local lambda) and 1 move (local -> runnable lambda). + } + trackedRunnable->Run(); + } + result |= Expect("NS_NewRunnableFunction with copyable&movable capture, copies", + copyCounter, 1); + result |= Expect("NS_NewRunnableFunction with copyable&movable capture, moves", + moveCounter, 1); + } + + return result; +} + int main(int argc, char** argv) { + int result = TestNS_NewRunnableFunction(); + ScopedXPCOM xpcom("ThreadUtils"); NS_ENSURE_FALSE(xpcom.failed(), 1); @@ -168,8 +385,6 @@ int main(int argc, char** argv) NS_ProcessPendingEvents(nullptr); } - int result = 0; - for (uint32_t i = 0; i < MAX_TESTS; i++) { if (gRunnableExecuted[i]) { passed("Test %d passed",i);