#! /usr/bin/env python
# -*- coding: iso-8859-1 -*-
# vi:ts=4:et
# $Id: test_internals.py,v 1.17 2003/05/01 16:48:54 mfx Exp $

#
# a simple self-test
#

try:
    # need Python 2.2 or better for garbage collection
    from gc import get_objects
    import gc
    del get_objects
    gc.enable()
except ImportError:
    gc = None
import copy, os, sys
from StringIO import StringIO
try:
    import cPickle
except ImportError:
    cPickle = None
try:
    import pickle
except ImportError:
    pickle = None

# update sys.path when running in the build directory
from util import get_sys_path
sys.path = get_sys_path()

import pycurl
from pycurl import Curl, CurlMulti


class opts:
    verbose = 1

if "-q" in sys.argv:
    opts.verbose = opts.verbose - 1


print "Python", sys.version
print "PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM)
print "PycURL version info", pycurl.version_info()
print "  %s, compiled %s" % (pycurl.__file__, pycurl.COMPILE_DATE)


# /***********************************************************************
# // test misc
# ************************************************************************/

if 1:
    c = Curl()
    assert c.URL is pycurl.URL
    del c


# /***********************************************************************
# // test handles
# ************************************************************************/

# remove an invalid handle: this should fail
if 1:
    m = CurlMulti()
    c = Curl()
    try:
        m.remove_handle(c)
    except pycurl.error:
        pass
    else:
        assert 0, "internal error"
    del m, c


# remove an invalid but closed handle
if 1:
    m = CurlMulti()
    c = Curl()
    c.close()
    m.remove_handle(c)
    del m, c


# add a closed handle: this should fail
if 1:
    m = CurlMulti()
    c = Curl()
    c.close()
    try:
        m.add_handle(c)
    except pycurl.error:
        pass
    else:
        assert 0, "internal error"
    m.close()
    del m, c


# add a handle twice: this should fail
if 1:
    m = CurlMulti()
    c = Curl()
    m.add_handle(c)
    try:
        m.add_handle(c)
    except pycurl.error:
        pass
    else:
        assert 0, "internal error"
    del m, c


# add a handle on multiple stacks: this should fail
if 1:
    m1 = CurlMulti()
    m2 = CurlMulti()
    c = Curl()
    m1.add_handle(c)
    try:
        m2.add_handle(c)
    except pycurl.error:
        pass
    else:
        assert 0, "internal error"
    del m1, m2, c


# move a handle
if 1:
    m1 = CurlMulti()
    m2 = CurlMulti()
    c = Curl()
    m1.add_handle(c)
    m1.remove_handle(c)
    m2.add_handle(c)
    del m1, m2, c


# /***********************************************************************
# // test copying and pickling - copying and pickling of
# // instances of Curl and CurlMulti is not allowed
# ************************************************************************/

if 1 and copy:
    c = Curl()
    m = CurlMulti()
    try:
        copy.copy(c)
    except copy.Error:
        pass
    else:
        assert 0, "internal error - copying should fail"
    try:
        copy.copy(m)
    except copy.Error:
        pass
    else:
        assert 0, "internal error - copying should fail"

if 1 and pickle:
    c = Curl()
    m = CurlMulti()
    fp = StringIO()
    p = pickle.Pickler(fp, 1)
    try:
        p.dump(c)
    except pickle.PicklingError:
        pass
    else:
        assert 0, "internal error - pickling should fail"
    try:
        p.dump(m)
    except pickle.PicklingError:
        pass
    else:
        assert 0, "internal error - pickling should fail"
    del c, m, fp, p

if 1 and cPickle:
    c = Curl()
    m = CurlMulti()
    fp = StringIO()
    p = cPickle.Pickler(fp, 1)
    try:
        p.dump(c)
    except cPickle.PicklingError:
        pass
    else:
        assert 0, "internal error - pickling should fail"
    try:
        p.dump(m)
    except cPickle.PicklingError:
        pass
    else:
        assert 0, "internal error - pickling should fail"
    del c, m, fp, p


# /***********************************************************************
# // test refcounts
# ************************************************************************/

# basic check of reference counting (use a memory checker like valgrind)
if 1:
    c = Curl()
    m = CurlMulti()
    m.add_handle(c)
    del m
    m = CurlMulti()
    c.close()
    del m, c

# basic check of cyclic garbage collection
if 1 and gc:
    gc.collect()
    c = Curl()
    c.m = CurlMulti()
    c.m.add_handle(c)
    # create some nasty cyclic references
    c.c = c
    c.c.c1 = c
    c.c.c2 = c
    c.c.c3 = c.c
    c.c.c4 = c.m
    c.m.c = c
    c.m.m = c.m
    c.m.c = c
    # delete
    gc.collect()
    flags = gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_OBJECTS
    if opts.verbose >= 1:
        flags = flags | gc.DEBUG_STATS
    gc.set_debug(flags)
    gc.collect()
    ##print gc.get_referrers(c)
    ##print gc.get_objects()
    if opts.verbose >= 1:
        print "Tracked objects:", len(gc.get_objects())
    # The `del' below should delete these 4 objects:
    #   Curl + internal dict, CurlMulti + internal dict
    del c
    gc.collect()
    if opts.verbose >= 1:
        print "Tracked objects:", len(gc.get_objects())

if 1:
    # Ensure that the refcounting error in "reset" is fixed:
    for i in xrange(100000):
        c = Curl()
        c.reset()

# /***********************************************************************
# // done
# ************************************************************************/

print "All tests passed."
