#include #include #include using namespace lw; class AllocPoolTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE( AllocPoolTest ); CPPUNIT_TEST( testUnused ); CPPUNIT_TEST( testAllocPartial ); CPPUNIT_TEST( testAllocFull ); CPPUNIT_TEST( testAllocNewList ); CPPUNIT_TEST( testFreePartialToFree ); CPPUNIT_TEST( testFreePartialToPartial ); CPPUNIT_TEST( testFreeFullToPartial ); CPPUNIT_TEST( testFreeLists ); CPPUNIT_TEST( testFreeFragmented ); CPPUNIT_TEST( testFreeMaxLists ); CPPUNIT_TEST( testReallocation ); CPPUNIT_TEST_SUITE_END( ); public: void setUp( ) override; void testUnused( ); void testAllocPartial( ); void testAllocFull( ); void testAllocNewList( ); void testFreePartialToFree( ); void testFreePartialToPartial( ); void testFreeFullToPartial( ); void testFreeLists( ); void testFreeFragmented( ); void testFreeMaxLists( ); void testReallocation( ); }; CPPUNIT_TEST_SUITE_REGISTRATION( AllocPoolTest ); /*----------------------------------------------------------------------------*/ namespace { class T_Test_ { public: static size_t counters[ 256 ]; uint8_t index; T_Test_( ) = delete; T_Test_( const uint8_t v ) noexcept : index( v ) { counters[ index ] ++; } T_Test_( T_Test_ const& other ) noexcept : index( other.index ) { counters[ index ] ++; } T_Test_( T_Test_&& other ) noexcept : index( other.index ) { counters[ index ] ++; } ~T_Test_( ) { counters[ index ] --; } void* operator new( size_t count ) noexcept; void operator delete( void* object ) noexcept; static size_t total( ) noexcept { size_t sum = 0; for ( auto i = 0 ; i < 255 ; i ++ ) { sum += counters[ i ]; } return sum; } }; M_CLASS_POINTERS( Test_ ); size_t T_Test_::counters[ 256 ]; static constexpr size_t PerList_ = 15; static constexpr size_t MaxFreeLists_ = 4; static constexpr size_t MFLObjects_ = PerList_ * MaxFreeLists_; using T_Pool_ = T_PoolAllocator< sizeof( T_Test_ ) , alignof( T_Test_ ) , PerList_ , MaxFreeLists_ >; static T_OwnPtr< T_Pool_ > Pool_; void InitPool_( ) { Pool_ = NewOwned< T_Pool_ >( ); } void KillPool_( ) { Pool_.clear( ); } void* T_Test_::operator new( const size_t count ) noexcept { assert( Pool_ ); return Pool_->allocate( count ); } void T_Test_::operator delete( void* object ) noexcept { assert( Pool_ ); Pool_->free( object ); } } #define M_CHECK_LISTS_( T , F , U ) \ do { \ CPPUNIT_ASSERT_EQUAL( size_t( T ) , Pool_->countFreeLists( ) ); \ CPPUNIT_ASSERT_EQUAL( size_t( F ) , Pool_->countPartialLists( ) ); \ CPPUNIT_ASSERT_EQUAL( size_t( U ) , Pool_->countFullLists( ) ); \ } while ( 0 ) #define M_CHECK_USAGE_( T , F , U ) \ do { \ size_t _t_ , _f_ , _u_; \ Pool_->getUsage( _t_ , _f_ , _u_ ); \ CPPUNIT_ASSERT_EQUAL( size_t( T ) , _t_ ); \ CPPUNIT_ASSERT_EQUAL( size_t( F ) , _f_ ); \ CPPUNIT_ASSERT_EQUAL( size_t( U ) , _u_ ); \ } while ( 0 ) #define M_CHECK_COUNT_( N ) \ CPPUNIT_ASSERT_EQUAL( size_t( N ) , T_Test_::total( ) ) void AllocPoolTest::setUp( ) { memset( T_Test_::counters , 0 , sizeof( T_Test_::counters ) ); } /*----------------------------------------------------------------------------*/ void AllocPoolTest::testUnused( ) { InitPool_( ); M_CHECK_LISTS_( 1 , 0 , 0 ); M_CHECK_USAGE_( PerList_ , PerList_ , 0 ); KillPool_( ); M_CHECK_COUNT_( 0 ); } /*----------------------------------------------------------------------------*/ void AllocPoolTest::testAllocPartial( ) { InitPool_( ); { OP_Test_ test( NewOwned< T_Test_ >( 0 ) ); M_CHECK_COUNT_( 1 ); M_CHECK_LISTS_( 0 , 1 , 0 ); M_CHECK_USAGE_( PerList_ , PerList_ - 1 , 1 ); } KillPool_( ); M_CHECK_COUNT_( 0 ); } void AllocPoolTest::testAllocFull( ) { InitPool_( ); { OP_Test_ test[ PerList_ ]; for ( auto i = 0u ; i < PerList_ ; i ++ ) { test[ i ] = NewOwned< T_Test_ >( i ); } M_CHECK_COUNT_( PerList_ ); M_CHECK_LISTS_( 0 , 0 , 1 ); M_CHECK_USAGE_( PerList_ , 0 , PerList_ ); } KillPool_( ); M_CHECK_COUNT_( 0 ); } void AllocPoolTest::testAllocNewList( ) { InitPool_( ); { OP_Test_ test[ PerList_ + 1 ]; for ( auto i = 0u ; i <= PerList_ ; i ++ ) { test[ i ] = NewOwned< T_Test_ >( i ); } M_CHECK_COUNT_( PerList_ + 1 ); M_CHECK_LISTS_( 0 , 1 , 1 ); M_CHECK_USAGE_( PerList_ * 2 , PerList_ - 1 , PerList_ + 1 ); } KillPool_( ); M_CHECK_COUNT_( 0 ); } /*----------------------------------------------------------------------------*/ void AllocPoolTest::testFreePartialToFree( ) { InitPool_( ); { OP_Test_ obj( NewOwned< T_Test_ >( 0 ) ); obj.clear( ); M_CHECK_COUNT_( 0 ); M_CHECK_LISTS_( 1 , 0 , 0 ); M_CHECK_USAGE_( PerList_ , PerList_ , 0 ); } KillPool_( ); } void AllocPoolTest::testFreePartialToPartial( ) { InitPool_( ); { OP_Test_ persist( NewOwned< T_Test_ >( 0 ) ); OP_Test_ test( NewOwned< T_Test_ >( 1 ) ); test.clear( ); M_CHECK_COUNT_( 1 ); M_CHECK_LISTS_( 0 , 1 , 0 ); M_CHECK_USAGE_( PerList_ , PerList_ - 1 , 1 ); } KillPool_( ); } void AllocPoolTest::testFreeFullToPartial( ) { InitPool_( ); { OP_Test_ objs[ PerList_ ]; for ( auto i = 0u ; i < PerList_ ; i ++ ) { objs[ i ] = NewOwned< T_Test_ >( i ); } objs[ PerList_ - 1 ].clear( ); M_CHECK_COUNT_( PerList_ - 1 ); M_CHECK_LISTS_( 0 , 1 , 0 ); M_CHECK_USAGE_( PerList_ , 1 , PerList_ - 1 ); } KillPool_( ); } /*----------------------------------------------------------------------------*/ void AllocPoolTest::testFreeLists( ) { InitPool_( ); { OP_Test_ obj[ MFLObjects_ ]; for ( auto i = 0u ; i < MFLObjects_ ; i ++ ) { obj[ i ] = NewOwned< T_Test_ >( i ); } for ( auto i = 0u ; i < MFLObjects_ ; i ++ ) { obj[ i ].clear( ); } M_CHECK_COUNT_( 0 ); M_CHECK_LISTS_( MaxFreeLists_ , 0 , 0 ); M_CHECK_USAGE_( MFLObjects_ , MFLObjects_ , 0 ); } KillPool_( ); } void AllocPoolTest::testFreeFragmented( ) { InitPool_( ); { OP_Test_ obj[ MFLObjects_ ]; for ( auto i = 0u ; i < MFLObjects_ ; i ++ ) { obj[ i ] = NewOwned< T_Test_ >( i ); } for ( auto i = 0u ; i < MaxFreeLists_ ; i ++ ) { obj[ i * PerList_ ].clear( ); } M_CHECK_COUNT_( MFLObjects_ - MaxFreeLists_ ); M_CHECK_LISTS_( 0 , MaxFreeLists_ , 0 ); M_CHECK_USAGE_( MFLObjects_ , MaxFreeLists_ , MFLObjects_ - MaxFreeLists_ ); } KillPool_( ); } void AllocPoolTest::testFreeMaxLists( ) { InitPool_( ); { OP_Test_ obj[ MFLObjects_ + PerList_ ]; for ( auto i = 0u ; i < MFLObjects_ + PerList_ ; i ++ ) { obj[ i ] = NewOwned< T_Test_ >( i ); } for ( auto i = 0u ; i < MFLObjects_ + PerList_ ; i ++ ) { obj[ i ].clear( ); } M_CHECK_COUNT_( 0 ); M_CHECK_LISTS_( MaxFreeLists_ , 0 , 0 ); M_CHECK_USAGE_( MFLObjects_ , MFLObjects_ , 0 ); } KillPool_( ); } /*----------------------------------------------------------------------------*/ void AllocPoolTest::testReallocation( ) { InitPool_( ); { OP_Test_ obj[ PerList_ ]; T_Test_* ptr[ PerList_ ]; for ( auto i = 0u ; i < PerList_ ; i ++ ) { obj[ i ] = NewOwned< T_Test_ >( i ); ptr[ i ] = obj[ i ].get( ); } for ( auto i = 1u ; i < PerList_ ; i += 2 ) { obj[ i ].clear( ); } for ( auto i = 1u ; i < PerList_ ; i += 2 ) { obj[ i ] = NewOwned< T_Test_ >( i ); CPPUNIT_ASSERT( obj[ i ].get( ) == ptr[ i ] ); } M_CHECK_LISTS_( 0 , 0 , 1 ); M_CHECK_USAGE_( PerList_ , 0 , PerList_ ); } KillPool_( ); }