/** @file

    $Id$

    $Date$
    $Author$

    @copy © Marc Wäckerlin
    @license LGPL, see file <a href="license.html">COPYING</a>

    $Log$
    Revision 1.1  2004/08/28 16:13:42  marc
    mrw-c++-0.92 (mrw)
    - new file: version.cxx
    - new file header for all sources
    - work around warning in mrw::auto<T>
    - possibility to compile without log4cxx
    - work around bugs in demangle.h and libiberty.h
    - corrections in documentation
    - added simple tracing mechanism
    - more warnings
    - small corrections in Auto<>::Free and a new test for it
    - possibility to compile without stack trace

*/
#include <mrw/smartpointer.hxx>
#include <cppunit/TestFixture.h>
#include <cppunit/ui/text/TestRunner.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/XmlOutputter.h>
#include <fstream>
#include <string>

class Content {
private:
  int& _drop;
  Content();
public:
  Content(int& drop): _drop(drop) {}
  virtual ~Content() {++_drop;}
};
class A: public Content {
  public:
    A(int& d): Content(d) {}
    virtual ~A() {}
    virtual void fn() {}
};
class B: public A {public: B(int& d): A(d) {}};
class C: public A {public: C(int& d): A(d) {}};
class SmartPointerTest:
  public mrw::SmartPointerParent, public CppUnit::TestFixture {
public:
  void SimpleConstructor() {
    mrw::SmartPointer<Content> p1;
    CPPUNIT_ASSERT(!(getPointer(p1) || getCounter(p1)));
  }
  void CopyConstructor() {
    mrw::SmartPointer<Content> p1;
    mrw::SmartPointer<Content> p2(p1);
    CPPUNIT_ASSERT(!(getPointer(p1) || getCounter(p1) ||
                     getPointer(p2) || getCounter(p2)));
  }
  void PointerConstructor() {
    int drops(0);
    mrw::SmartPointer<Content> p1(0);
    mrw::SmartPointer<Content> p2(new Content(drops));
    CPPUNIT_ASSERT(!(getPointer(p1) || getCounter(p1) ||
                     drops!=0 || !getPointer(p2) || !getCounter(p2) ||
                     getCounter(p2)->get()!=1));
  }
  void CastConstructor() {
    int drops1(0);
    mrw::SmartPointer<A> pa(new B(drops1));
    mrw::SmartPointer<B> pb(pa);
    mrw::SmartPointer<C> pc(pa);
    mrw::SmartPointer<A> pa2(pb);
    CPPUNIT_ASSERT(!(drops1!=0 || !getPointer(pa) || !getCounter(pa) ||
                     getPointer(pb)!=getPointer(pa) ||
                     getCounter(pb)!=getCounter(pa) ||
                     getPointer(pc) || getCounter(pc) ||
                     getCounter(pa)->get()!=3 ||
                     getPointer(pa2)!=getPointer(pa) ||
                     getCounter(pa2)!=getCounter(pa)));
  }
  void Destructor() {
    int drops(0);
    mrw::SmartPointer<Content>* p1 =
      new mrw::SmartPointer<Content>(new Content(drops));
    delete p1;
    CPPUNIT_ASSERT(!(drops!=1));
  }
  void CopyAssign() {
    int drops1(0);
    int drops2(0);
    mrw::SmartPointer<Content> p1(new Content(drops1));
    mrw::SmartPointer<Content> p2(new Content(drops2));
    p2 = p1;
    p1 = p2;
    CPPUNIT_ASSERT(!(drops1!=0 || !getPointer(p1) || !getCounter(p1) ||
                     getCounter(p1)->get()!=2 ||
                     getPointer(p1)!=getPointer(p2) ||
                     drops2!=1 || !getPointer(p2) || !getCounter(p2) ||
                     getCounter(p2)->get()!=2 ||
                     getCounter(p1)!=getCounter(p2)));
  }
  void PointerAssign() {
    int drops1(0);
    int drops2(0);
    int drops3(0);
    mrw::SmartPointer<Content> p1(new Content(drops1));
    mrw::SmartPointer<Content> p2(0);
    p1 = new Content(drops2);
    p1 = 0;
    p2 = new Content(drops3);
    CPPUNIT_ASSERT(!(drops1!=1 || getPointer(p1) || getCounter(p1) ||
                     drops2!=1 || !getPointer(p2) || !getCounter(p2) ||
                     drops3!=0 || getCounter(p2)->get()!=1));
  }
  void CastAssign() {
    int drops1(0);
    int drops2(0);
    int drops3(0);
    mrw::SmartPointer<A> pa(new B(drops1));
    mrw::SmartPointer<B> pb(new B(drops2));
    mrw::SmartPointer<C> pc(new C(drops3));
    pa = pa;
    pa = pb;
    pa = pc;
    pb = pa;
    pc = pa;
    CPPUNIT_ASSERT(!(drops1!=1 || drops2!=1 || drops3!=0 ||
                     !getPointer(pa) || getPointer(pa)!=getPointer(pc) ||
                     !getCounter(pa) || getCounter(pa)!=getCounter(pc) ||
                     getCounter(pa)->get()!=2 || getPointer(pb) ||
                     getCounter(pb)));
  }
  void PointerAccess() {
    mrw::SmartPointer<std::string> p1(new std::string);
    mrw::SmartPointer<std::string> p2;
    *p1 = "Hallo Welt!";
    CPPUNIT_ASSERT(!(p1.operator->()!=&*p1 || p1->find("Welt")!=6 ||
                     p2.operator->()));
  }
  void OperatorBool() {
    mrw::SmartPointer<std::string> p1(new std::string);
    mrw::SmartPointer<std::string> p2;
    CPPUNIT_ASSERT(!(!p1 || p2));
  }
  CPPUNIT_TEST_SUITE(SmartPointerTest);
  CPPUNIT_TEST(SimpleConstructor);
  CPPUNIT_TEST(CopyConstructor);
  CPPUNIT_TEST(PointerConstructor);
  CPPUNIT_TEST(CastConstructor);
  CPPUNIT_TEST(Destructor);
  CPPUNIT_TEST(CopyAssign);
  CPPUNIT_TEST(PointerAssign);
  CPPUNIT_TEST(CastAssign);
  CPPUNIT_TEST(PointerAccess);
  CPPUNIT_TEST(OperatorBool);
  CPPUNIT_TEST_SUITE_END();
};
CPPUNIT_TEST_SUITE_REGISTRATION(SmartPointerTest);

int main(int argc, char** argv) try {
  std::ofstream ofs((*argv+std::string(".xml")).c_str());
  CppUnit::TextUi::TestRunner runner;
  runner.setOutputter(new CppUnit::XmlOutputter(&runner.result(), ofs));
  runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest());
  return runner.run() ? 0 : 1;
 } catch (std::exception& e) {
  std::cerr<<"***Exception: "<<e.what()<<std::endl;
  return 1;
 }