//
//  esft_regtest.cpp
//
//  A regression test for enable_shared_from_this
//
//  Copyright (c) 2008 Peter Dimov
//
//  Distributed under the Boost Software License, Version 1.0.
//
//  See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//

#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/detail/lightweight_test.hpp>
#include <boost/config.hpp>
#include <memory>
#include <string>

class X: public boost::enable_shared_from_this< X >
{
private:

    int destroyed_;
    int deleted_;
    int expected_;

private:

    X( X const& );
    X& operator=( X const& );

public:

    static int instances;

public:

    explicit X( int expected ): destroyed_( 0 ), deleted_( 0 ), expected_( expected )
    {
        ++instances;
    }

    ~X()
    {
        BOOST_TEST( deleted_ == expected_ );
        BOOST_TEST( destroyed_ == 0 );
        ++destroyed_;
        --instances;
    }

    typedef void (*deleter_type)( X* );

    static void deleter( X * px )
    {
        ++px->deleted_;
    }

    static void deleter2( X * px )
    {
        ++px->deleted_;
        delete px;
    }
};

int X::instances = 0;

void test()
{
    BOOST_TEST( X::instances == 0 );

    {
        X x( 0 );
        BOOST_TEST( X::instances == 1 );
    }

    BOOST_TEST( X::instances == 0 );

#if !defined( BOOST_NO_AUTO_PTR )

    {
        std::auto_ptr<X> px( new X( 0 ) );
        BOOST_TEST( X::instances == 1 );
    }

#endif

    BOOST_TEST( X::instances == 0 );

    {
        boost::shared_ptr<X> px( new X( 0 ) );
        BOOST_TEST( X::instances == 1 );

        boost::weak_ptr<X> wp( px );
        BOOST_TEST( !wp.expired() );

        px.reset();

        BOOST_TEST( wp.expired() );
    }

    BOOST_TEST( X::instances == 0 );

    {
        X x( 1 );
        boost::shared_ptr<X> px( &x, X::deleter );
        BOOST_TEST( X::instances == 1 );

        X::deleter_type * pd = boost::get_deleter<X::deleter_type>( px );
        BOOST_TEST( pd != 0 && *pd == X::deleter );

        boost::weak_ptr<X> wp( px );
        BOOST_TEST( !wp.expired() );

        px.reset();

        BOOST_TEST( wp.expired() );
    }

    BOOST_TEST( X::instances == 0 );

    {
        boost::shared_ptr<X> px( new X( 1 ), X::deleter2 );
        BOOST_TEST( X::instances == 1 );

        X::deleter_type * pd = boost::get_deleter<X::deleter_type>( px );
        BOOST_TEST( pd != 0 && *pd == X::deleter2 );

        boost::weak_ptr<X> wp( px );
        BOOST_TEST( !wp.expired() );

        px.reset();

        BOOST_TEST( wp.expired() );
    }

    BOOST_TEST( X::instances == 0 );
}

struct V: public boost::enable_shared_from_this<V>
{
    virtual ~V() {}
    std::string m_;
};

struct V2
{
    virtual ~V2() {}
    std::string m2_;
};

struct W: V2, V
{
};

void test2()
{
    boost::shared_ptr<W> p( new W );
}

void test3()
{
    V * p = new W;
    boost::shared_ptr<void> pv( p );
    BOOST_TEST( pv.get() == p );
    BOOST_TEST( pv.use_count() == 1 );
}

struct null_deleter
{
    void operator()( void const* ) const {}
};

void test4()
{
    boost::shared_ptr<V> pv( new V );
    boost::shared_ptr<V> pv2( pv.get(), null_deleter() );
    BOOST_TEST( pv2.get() == pv.get() );
    BOOST_TEST( pv2.use_count() == 1 );
}

void test5()
{
    V v;

    boost::shared_ptr<V> p1( &v, null_deleter() );
    BOOST_TEST( p1.get() == &v );
    BOOST_TEST( p1.use_count() == 1 );

    try
    {
        p1->shared_from_this();
    }
    catch( ... )
    {
        BOOST_ERROR( "p1->shared_from_this() failed" );
    }

    p1.reset();

    boost::shared_ptr<V> p2( &v, null_deleter() );
    BOOST_TEST( p2.get() == &v );
    BOOST_TEST( p2.use_count() == 1 );

    try
    {
        p2->shared_from_this();
    }
    catch( ... )
    {
        BOOST_ERROR( "p2->shared_from_this() failed" );
    }
}

int main()
{
    test();
    test2();
    test3();
    test4();
    test5();

    return boost::report_errors();
}
