From 311e77440f11dbe8e49d75b09a4ae0f850ce00c5 Mon Sep 17 00:00:00 2001 From: Daniel Robertson Date: Thu, 27 Aug 2015 17:55:55 -0400 Subject: o3tl: add another unit test to cow_wrapper Add unit tests to cow_wrapper for the move ctor and move assignment. Change-Id: I82a5886ca7ae110985c7202125699cf95b6466d8 Reviewed-on: https://gerrit.libreoffice.org/18108 Reviewed-by: Thorsten Behrens Tested-by: Thorsten Behrens --- include/o3tl/cow_wrapper.hxx | 15 +++++++- o3tl/qa/cow_wrapper_clients.cxx | 50 ++++++++++++++++++++++++ o3tl/qa/cow_wrapper_clients.hxx | 56 +++++++++++++++++++++++++++ o3tl/qa/test-cow_wrapper.cxx | 84 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 203 insertions(+), 2 deletions(-) diff --git a/include/o3tl/cow_wrapper.hxx b/include/o3tl/cow_wrapper.hxx index 3f117a2b395b..da822b2c88b8 100644 --- a/include/o3tl/cow_wrapper.hxx +++ b/include/o3tl/cow_wrapper.hxx @@ -113,9 +113,11 @@ class cow_wrapper_client public: cow_wrapper_client(); cow_wrapper_client( const cow_wrapper_client& ); + cow_wrapper_client( cow_wrapper_client&& ); ~cow_wrapper_client(); cow_wrapper_client& operator=( const cow_wrapper_client& ); + cow_wrapper_client& operator=( cow_wrapper_client&& ); void modify( int nVal ); int queryUnmodified() const; @@ -144,6 +146,10 @@ cow_wrapper_client::cow_wrapper_client( const cow_wrapper_client& rSrc ) : maImpl( rSrc.maImpl ) { } +cow_wrapper_client::cow_wrapper_client( cow_wrapper_client& rSrc ) : + maImpl( std::move( rSrc.maImpl ) ) +{ +} cow_wrapper_client::~cow_wrapper_client() { } @@ -152,6 +158,11 @@ cow_wrapper_client& cow_wrapper_client::operator=( const cow_wrapper_client& rSr maImpl = rSrc.maImpl; return *this; } +cow_wrapper_client& cow_wrapper_client::operator=( cow_wrapper_client&& rSrc ) +{ + maImpl = std::move( rSrc.maImpl ); + return *this; +} void cow_wrapper_client::modify( int nVal ) { maImpl->setValue( nVal ); @@ -272,13 +283,13 @@ int cow_wrapper_client::queryUnmodified() const /// true, if not shared with any other cow_wrapper instance bool is_unique() const // nothrow { - return m_pimpl->m_ref_count == 1; + return m_pimpl ? m_pimpl->m_ref_count == 1 : true; } /// return number of shared instances (1 for unique object) typename MTPolicy::ref_count_t use_count() const // nothrow { - return m_pimpl->m_ref_count; + return m_pimpl ? m_pimpl->m_ref_count : 0; } void swap(cow_wrapper& r) // never throws diff --git a/o3tl/qa/cow_wrapper_clients.cxx b/o3tl/qa/cow_wrapper_clients.cxx index d49dd092865b..3cea25766c84 100644 --- a/o3tl/qa/cow_wrapper_clients.cxx +++ b/o3tl/qa/cow_wrapper_clients.cxx @@ -219,6 +219,56 @@ bool cow_wrapper_client4::operator<( const cow_wrapper_client4& rRHS ) const return maImpl < rRHS.maImpl; } +bool BogusRefCountPolicy::s_bShouldIncrement = 0; +bool BogusRefCountPolicy::s_bShouldDecrement = 0; +sal_uInt32 BogusRefCountPolicy::s_nEndOfScope = 0; + +cow_wrapper_client5::cow_wrapper_client5() : + maImpl() +{ +} + +cow_wrapper_client5::cow_wrapper_client5(int nX) : + maImpl(nX) +{ +} + +cow_wrapper_client5::cow_wrapper_client5( const cow_wrapper_client5& rSrc ) : + maImpl( rSrc.maImpl ) +{ +} + +cow_wrapper_client5::cow_wrapper_client5( cow_wrapper_client5&& rSrc ) : + maImpl( std::move( rSrc.maImpl ) ) +{ +} + +cow_wrapper_client5::~cow_wrapper_client5() +{ +} + +cow_wrapper_client5& cow_wrapper_client5::operator=( const cow_wrapper_client5& rSrc ) +{ + maImpl = rSrc.maImpl; + + return *this; +} + +cow_wrapper_client5& cow_wrapper_client5::operator=( cow_wrapper_client5&& rSrc ) +{ + maImpl = std::move( rSrc.maImpl ); + + return *this; +} + +bool cow_wrapper_client5::operator==( const cow_wrapper_client5& rSrc ) const { + return maImpl == rSrc.maImpl; +} + +bool cow_wrapper_client5::operator!=( const cow_wrapper_client5& rSrc ) const { + return maImpl != rSrc.maImpl; +} + } // namespace o3tltests /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/o3tl/qa/cow_wrapper_clients.hxx b/o3tl/qa/cow_wrapper_clients.hxx index 30dd5e043732..f438c06a20ab 100644 --- a/o3tl/qa/cow_wrapper_clients.hxx +++ b/o3tl/qa/cow_wrapper_clients.hxx @@ -21,6 +21,7 @@ #define INCLUDED_O3TL_QA_COW_WRAPPER_CLIENTS_HXX #include "o3tl/cow_wrapper.hxx" +#include "cppunit/extensions/HelperMacros.h" /* Definition of Cow_Wrapper_Clients classes */ @@ -139,6 +140,61 @@ private: o3tl::cow_wrapper< int > maImpl; }; +// singleton ref-counting policy used to keep track of when +// incrementing and decrementing occurs +struct BogusRefCountPolicy +{ + static bool s_bShouldIncrement; + static bool s_bShouldDecrement; + static sal_uInt32 s_nEndOfScope; + typedef sal_uInt32 ref_count_t; + static void incrementCount( ref_count_t& rCount ) { + if(s_bShouldIncrement) + { + ++rCount; + s_bShouldIncrement = 0; + } + else + CPPUNIT_FAIL("Ref-counting policy incremented when it should not have."); + } + static bool decrementCount( ref_count_t& rCount ) { + if(s_nEndOfScope) + { + --rCount; + --s_nEndOfScope; + return true; + } + if(s_bShouldDecrement) + { + --rCount; + s_bShouldDecrement = 0; + } + else + CPPUNIT_FAIL("Ref-counting policy decremented when it should not have."); + return true; + } +}; + +class cow_wrapper_client5 +{ +public: + cow_wrapper_client5(); + explicit cow_wrapper_client5(int); + ~cow_wrapper_client5(); + + cow_wrapper_client5( const cow_wrapper_client5& ); + cow_wrapper_client5( cow_wrapper_client5&& ); + cow_wrapper_client5& operator=( const cow_wrapper_client5& ); + cow_wrapper_client5& operator=( cow_wrapper_client5&& ); + + sal_uInt32 use_count() const { return maImpl.use_count(); } + + bool operator==( const cow_wrapper_client5& rRHS ) const; + bool operator!=( const cow_wrapper_client5& rRHS ) const; + +private: + o3tl::cow_wrapper< int, BogusRefCountPolicy > maImpl; +}; } // namespace o3tltests #endif // INCLUDED_O3TL_QA_COW_WRAPPER_CLIENTS_HXX diff --git a/o3tl/qa/test-cow_wrapper.cxx b/o3tl/qa/test-cow_wrapper.cxx index 82bc2be8d91b..71003551907e 100644 --- a/o3tl/qa/test-cow_wrapper.cxx +++ b/o3tl/qa/test-cow_wrapper.cxx @@ -161,6 +161,89 @@ public: !aTestObj1.is_default() ); } + void testRefCounting() + { + // add scope to ensure appropriate number of calls to + // the reference counting policy have been made + { + // if any incrementing/decrementing occurs a failure + // will occur + cow_wrapper_client5 aTestObj1(1); + cow_wrapper_client5 aTestObj2( std::move( aTestObj1 ) ); + CPPUNIT_ASSERT_MESSAGE("aTestObj2.use_count() == 1", + aTestObj2.use_count() == 1 ); + + // the following should increment + BogusRefCountPolicy::s_bShouldIncrement = 1; + cow_wrapper_client5 aTestObj3( aTestObj2 ); + CPPUNIT_ASSERT_MESSAGE("s_bShouldIncrement == 0", + BogusRefCountPolicy::s_bShouldIncrement == 0 ); + + CPPUNIT_ASSERT_MESSAGE("aTestObj3.use_count() == 2", + aTestObj3.use_count() == 2 ); + { + cow_wrapper_client5 aTestObj4; + // the following should decrement the lvalue and then increment the rvalue + BogusRefCountPolicy::s_bShouldIncrement = 1; + BogusRefCountPolicy::s_bShouldDecrement = 1; + aTestObj4 = aTestObj2; + CPPUNIT_ASSERT_MESSAGE("s_bShouldIncrement == 0", + BogusRefCountPolicy::s_bShouldIncrement == 0 ); + CPPUNIT_ASSERT_MESSAGE("s_bShouldDecrement == 0", + BogusRefCountPolicy::s_bShouldDecrement == 0 ); + + CPPUNIT_ASSERT_MESSAGE("aTestObj2.use_count() == 3", + aTestObj2.use_count() == 3 ); + CPPUNIT_ASSERT_MESSAGE("aTestObj3.use_count() == 3", + aTestObj3.use_count() == 3 ); + CPPUNIT_ASSERT_MESSAGE("aTestObj4.use_count() == 3", + aTestObj4.use_count() == 3 ); + CPPUNIT_ASSERT_MESSAGE("aTestObj2 == aTestObj3", + aTestObj2 == aTestObj3 ); + CPPUNIT_ASSERT_MESSAGE("aTestObj3 == aTestObj4", + aTestObj3 == aTestObj4 ); + CPPUNIT_ASSERT_MESSAGE("aTestObj2 == aTestObj4", + aTestObj2 == aTestObj4 ); + + // only decrement the lvalue before assignment + BogusRefCountPolicy::s_bShouldDecrement = 1; + aTestObj4 = cow_wrapper_client5( 4 ); + CPPUNIT_ASSERT_MESSAGE("s_bShouldIncrement == 0", + BogusRefCountPolicy::s_bShouldIncrement == 0 ); + + // only one call should be made to the ref counting policy's + // decrementing function at the end of the scope + BogusRefCountPolicy::s_bShouldDecrement = 1; + } + CPPUNIT_ASSERT_MESSAGE("s_bShouldDecrement == 0", + BogusRefCountPolicy::s_bShouldDecrement == 0 ); + + // self assignment + // aTestObj2 is defunct afterwards, one decrement happens + BogusRefCountPolicy::s_bShouldDecrement = 1; + aTestObj3 = std::move( aTestObj2 ); + CPPUNIT_ASSERT_MESSAGE("aTestObj2.use_count() == 0", + aTestObj2.use_count() == 0 ); + CPPUNIT_ASSERT_MESSAGE("aTestObj3.use_count() == 1", + aTestObj3.use_count() == 1 ); + + cow_wrapper_client5 aTestObj5; + + // only decrement the lvalue before assignment + BogusRefCountPolicy::s_bShouldDecrement = 1; + aTestObj3 = std::move( aTestObj5 ); + CPPUNIT_ASSERT_MESSAGE("s_bShouldDecrement == 0", + BogusRefCountPolicy::s_bShouldDecrement == 0); + + // one call should be made to the ref-counting policy's + // decrementing function at the end of the scope. Only + // aTestObj3 still holds a valid instance + BogusRefCountPolicy::s_nEndOfScope = 1; + } + CPPUNIT_ASSERT_MESSAGE("s_EndOfScope == 0", + BogusRefCountPolicy::s_nEndOfScope == 0 ); + } + // Change the following lines only, if you add, remove or rename // member functions of the current class, // because these macros are need by auto register mechanism. @@ -168,6 +251,7 @@ public: CPPUNIT_TEST_SUITE(cow_wrapper_test); CPPUNIT_TEST(testCowWrapper); CPPUNIT_TEST(testStaticDefault); + CPPUNIT_TEST(testRefCounting); CPPUNIT_TEST_SUITE_END(); }; -- cgit