/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Test of shptr<> template class.
 *
 * --------------------------------------------------------------------
 * -- This is common code for shptr.cc and RefPtr.cc (osg::ref_ptr)  --
 * --------------------------------------------------------------------
 *
 * [2008/05] This test is intended to test a generic implementation
 * of an intrusive reference-count template class.
 *
 * But, optionally, if TEST_REF_COUNT is set, then the value
 * of the reference-count will be tested also
 * (testing then becomes specific to osg::ref_ptr).
 *
 *//*
 * LEGAL:   COPYRIGHT (C) 2008 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#define TESTING 1
#include "test.hh"
#include "base/module.hh"
#include "base/types.hh"
using namespace base;
#if COMPILE_THREADS
#include "threads/module.hh"
#include "threads/thptr.hh"
using namespace threads;
#endif
#if ! TEST_OSG_REF_PTR
#include "test_shptr.hh"
#endif

#if TEST_REF_COUNT
# if TEST_OSG_REF_PTR
#define GET_REF_COUNT( SHPTR ) ((SHPTR)->referenceCount())  // OSG
# else
#define GET_REF_COUNT( SHPTR ) ((SHPTR)->GetRefCount())
# endif
#endif

//------------------------------------------------------------------------------

#undef shptr
#undef Shared
#define shptr  TEST_SHPTR_CLASS
#define Shared TEST_SHARED_CLASS

////////////////////////////////////////////////////////////////////////////////
//
class Object : public Shared
{
public:
                        Object( int i ) : mVal(i) { ++msInstanceCount; }
                        ~Object() { --msInstanceCount; }
    CLASS_METHOD void   Clear( void ) { msInstanceCount = 0; }
    int                 Get( void ) { return mVal; }

public:
    CLASS_VAR int   msInstanceCount;
    int             mVal;
};

int Object::msInstanceCount;

/*****************************************************************************
 * Test that shptr auto-destroys after leaving block.
 *****************************************************************************/
bool
Test1( void )
{
    Object::msInstanceCount = 0;
  {
    shptr<Object> obj = new Object( 123 );
    if ( Object::msInstanceCount != 1 ) return Fail();

#if TEST_REF_COUNT
    if ( GET_REF_COUNT(obj) != 1 ) return Fail();
#endif
  }
    if ( Object::msInstanceCount != 0 ) return Fail();

    return true;
}

/*****************************************************************************
 * Test passing a shptr.
 *****************************************************************************/
void
Test2Sub( shptr<Object> obj )
{
    if ( Object::msInstanceCount != 1 ) Fail();

#if TEST_REF_COUNT
    if ( GET_REF_COUNT(obj) != 2 ) Fail();
#endif
}

bool
Test2( void )
{
    Object::msInstanceCount = 0;
  {
    shptr<Object> obj = new Object( 123 );
#if TEST_REF_COUNT
    if ( GET_REF_COUNT(obj) != 1 ) return Fail();
#endif
    if ( Object::msInstanceCount != 1 ) return Fail();
    Test2Sub( obj );
    if ( Object::msInstanceCount != 1 ) return Fail();
  }
    if ( Object::msInstanceCount != 0 ) return Fail();

    return true;
}

/*****************************************************************************
 * Test storing a shptr in a persistent object.
 *****************************************************************************/
void
Test3Sub( shptr<Object> obj )
{
#if TEST_REF_COUNT
    if ( GET_REF_COUNT(obj) != 2 ) Fail();  // 2 = caller, arg
#endif

    PERSISTENT shptr<Object> sObj = obj;

#if TEST_REF_COUNT
    if ( GET_REF_COUNT(obj) != 3 ) Fail();  // 3 = caller, arg, PERSISTENT
#endif

    if ( Object::msInstanceCount != 1 ) Fail();
}

bool
Test3( void )
{
    Object::msInstanceCount = 0;
  {
    shptr<Object> obj = new Object( 123 );

#if TEST_REF_COUNT
    if ( GET_REF_COUNT(obj) != 1 ) Fail();  // 1 = caller
#endif

    if ( Object::msInstanceCount != 1 ) return Fail();

    Test3Sub( obj );

#if TEST_REF_COUNT
    if ( GET_REF_COUNT(obj) != 2 ) Fail();  // 2 = caller, PERSISTENT
#endif

    if ( Object::msInstanceCount != 1 ) return Fail();
  }
    if ( Object::msInstanceCount != 1 ) return Fail();  // still has refCount

    return true;
}

/*****************************************************************************
 * Test returning a shptr.
 *****************************************************************************/
shptr<Object>
Test4Sub( void )
{
    if ( Object::msInstanceCount != 0 ) Fail();
    return new Object( 123 );
}

bool
Test4( void )
{
    Object::msInstanceCount = 0;
 {
    if ( Object::msInstanceCount != 0 ) Fail();
    shptr<Object> obj = Test4Sub();
    if ( Object::msInstanceCount != 1 ) Fail();

#if TEST_REF_COUNT
    if ( GET_REF_COUNT(obj) != 1 ) Fail();
#endif
 }
    if ( Object::msInstanceCount != 0 ) Fail();

    return true;
}

/*****************************************************************************
 * 
 *****************************************************************************/
int main( int argc, char** argv )
{
    RUN_TEST( TEST_NAME, TEST_ERROR_MODE_ABORT,
              Test1()
          and Test2()
          and Test3()
          and Test4() )
}
