<commit_before>import os
import shutil
import tempfile

from mock import patch
from mock import Mock

from zope.interface import implements

from twisted.trial import unittest
from twisted.test import proto_helpers
from twisted.internet import defer, error, task, tcp
from twisted.internet.endpoints import TCP4ServerEndpoint, serverFromString, clientFromString
from twisted.python.failure import Failure
from twisted.internet.interfaces import IReactorCore, IProtocolFactory, IProtocol, IReactorTCP, IListeningPort, IAddress

from txtorcon import TorControlProtocol
from txtorcon import ITorControlProtocol
from txtorcon import TorConfig
from txtorcon import launch_tor
from txtorcon import TCPHiddenServiceEndpoint
from txtorcon import TorNotFound
from txtorcon import TCPHiddenServiceEndpointParser
from txtorcon import IProgressProvider
from txtorcon import TorOnionAddress
from txtorcon.endpoints import NoOpProtocolFactory
from txtorcon.endpoints import get_global_tor                       # FIXME


class EndpointTests(unittest.TestCase):

    def setUp(self):
        from txtorcon import endpoints
        endpoints._global_tor_config = None
        del endpoints._global_tor_lock
        endpoints._global_tor_lock = defer.DeferredLock()
        self.reactor = FakeReactorTcp(self)
        self.protocol = FakeControlProtocol([])
        self.config = TorConfig(self.protocol)
        self.protocol.answers.append('config/names=\nHiddenServiceOptions Virtual')
        self.protocol.answers.append('HiddenServiceOptions')

    def tearDown(self):
        from txtorcon import endpoints
        endpoints._global_tor_config = None
        del endpoints._global_tor_lock
        endpoints._global_tor_lock = defer.DeferredLock()

    @defer.inlineCallbacks
    def test_global_tor_error(self):
        config0 = yield get_global_tor(Mock(), _tor_launcher=lambda x, y, z: True)
        # now if we specify a control_port it should be an error since
        # the above should have launched one.
        try:
            config1 = yield get_global_tor(Mock(), control_port=111, _tor_launcher=lambda x, y, z: True)
            self.fail()
        except RuntimeError as e:
            # should be an error
            pass

    @defer.inlineCallbacks
    def test_endpoint_properties(self):
        ep = yield TCPHiddenServiceEndpoint.private_tor(Mock(), 80)
        self.assertEqual(None, ep.onion_private_key)
        self.assertEqual(None, ep.onion_uri)
        ep.hiddenservice = Mock()
        ep.hiddenservice.private_key = 'mumble'
        self.assertEqual('mumble', ep.onion_private_key)

    @defer.inlineCallbacks
    def test_private_tor(self):
        m = Mock()
        from txtorcon import endpoints
        endpoints.launch_tor = m
        ep = yield TCPHiddenServiceEndpoint.private_tor(Mock(), 80, control_port=1234)
        m.assert_called()

    @defer.inlineCallbacks
    def test_private_tor_no_control_port(self):
        m = Mock()
        from txtorcon import endpoints
        endpoints.launch_tor = m
        ep = yield TCPHiddenServiceEndpoint.private_tor(Mock(), 80)
        m.assert_called()

    @defer.inlineCallbacks
    def test_system_tor(self):
        from test_torconfig import FakeControlProtocol
        def boom(*args):
            # why does the new_callable thing need a callable that
            # returns a callable? Feels like I must be doing something
            # wrong somewhere...
            def bam(*args, **kw):
                return self.protocol
            return bam
        with patch('txtorcon.endpoints.launch_tor') as m:
            with patch('txtorcon.endpoints.build_tor_connection', new_callable=boom) as btc:
                ep = yield TCPHiddenServiceEndpoint.system_tor(self.reactor,
                                                           clientFromString(self.reactor, "tcp:localhost:port=9050"), 80)
                port = yield ep.listen(NoOpProtocolFactory())
                toa = port.getHost()
                self.assertTrue(hasattr(toa, 'onion_uri'))
                self.assertTrue(hasattr(toa, 'onion_port'))
                port.startListening()
                str(port)
                port.tor_config
                m.assert_called()
        

    @defer.inlineCallbacks
    def test_basic(self):
        listen = RuntimeError("listen")
        connect = RuntimeError("connect")
        reactor = proto_helpers.RaisingMemoryReactor(listen, connect)
        reactor.addSystemEventTrigger = Mock()

        ep = TCPHiddenServiceEndpoint(reactor, self.config, 123)
        self.config.bootstrap()
        yield self.config.post_bootstrap
        self.assertTrue(IProgressProvider.providedBy(ep))

        try:
            port = yield ep.listen(NoOpProtocolFactory())
            self.fail("Should have been an exception")
        except RuntimeError as e:
            # make sure we called listenTCP not connectTCP
            self.assertEqual(e, listen)

        #self.assertIsInstance(port, torconfig.TorOnionListeningPort)
        #self.assertEqual('127.0.0.1', ep.tcp_endpoint._interface)
        ## make sure _ListWrapper's __repr__ doesn't explode
        repr(self.config.HiddenServices)

    def test_progress_updates(self):
        config = TorConfig()
        ep = TCPHiddenServiceEndpoint(self.reactor, config, 123)

        self.assertTrue(IProgressProvider.providedBy(ep))
        prog = IProgressProvider(ep)
        ding = Mock()
        prog.add_progress_listener(ding)
        args = (50, "blarg", "Doing that thing we talked about.")
        # kind-of cheating, test-wise?
        ep._tor_progress_update(*args)
        ding.assert_called_with(*args)
        

    def __test_listen_already_used_port(self):
        """Ensure that we call the port_generator() again if we can't listen on a given port"""
        self._called = 0
        def generate_two_ports():
            self._called += 1
            yield 123
            self._called += 1
            yield 456
        port_gen = generate_two_ports()

        self.reactor.failures = 1
        ep = TCPHiddenServiceEndpoint(self.reactor, self.config, 999,
                                      port_generator=port_gen.next)
        d = ep.listen(NoOpProtocolFactory())
        self.config.bootstrap()
        self.assertEqual(self._called, 2)
        self.assertEqual('1172.16.31.10', ep.tcp_endpoint._interface)

        def confirm_listening_port(arg):
            self.assertEqual(456, arg.get_local_address().port)
        d.addCallback(confirm_listening_port)
        return d
        

    def test_multiple_listen(self):
        ep = TCPHiddenServiceEndpoint(self.reactor, self.config, 123)
        d0 = ep.listen(NoOpProtocolFactory())

        @defer.inlineCallbacks
        def more_listen(arg):
            yield arg.stopListening()
            d1 = ep.listen(NoOpProtocolFactory())

            def foo(arg):
                return arg
            d1.addBoth(foo)
            defer.returnValue(arg)
            return
        d0.addBoth(more_listen)
        self.config.bootstrap()

        def check(arg):
            self.assertEqual('127.0.0.1', ep.tcp_endpoint._interface)
        d0.addCallback(check).addErrback(self.fail)
        return d0

    def test_already_bootstrapped(self):
        self.config.bootstrap()

        ep = TCPHiddenServiceEndpoint(self.reactor, self.config, 123)
        d = ep.listen(NoOpProtocolFactory())
        return d

    @defer.inlineCallbacks
    def test_explicit_data_dir(self):
        config = TorConfig()
        ep = TCPHiddenServiceEndpoint(self.reactor, config, 123, '/mumble/mumble')

        # fake out some things so we don't actually have to launch + bootstrap
        class FakeTorProcessProtocol(object):
            tor_protocol = self.reactor.protocol
        process = FakeTorProcessProtocol()
        ep._launch_tor = Mock(return_value=process)
        config._update_proto(Mock())
        config.bootstrap()
        yield config.post_bootstrap

        # make sure listen() correctly configures our hidden-serivce
        # with the explicit directory we passed in above
        port = yield ep.listen(NoOpProtocolFactory())
        self.assertEqual(1, len(config.HiddenServices))
        self.assertEqual(config.HiddenServices[0].dir, '/mumble/mumble')

    def test_failure(self):
        self.reactor.failures = 1
        ep = TCPHiddenServiceEndpoint(self.reactor, self.config, 123)
        d = ep.listen(NoOpProtocolFactory())
        self.config.bootstrap()
        d.addErrback(self.check_error)
        return d

    def check_error(self, failure):
        self.assertEqual(failure.type, error.CannotListenError)
        return None

    def test_parse_via_plugin(self):
        # make sure we have a valid thing from get_global_tor without actually launching tor
        config = TorConfig()
        config.post_bootstrap = defer.succeed(config)
        from txtorcon import torconfig
        torconfig._global_tor_config = None
        get_global_tor(self.reactor,
                       _tor_launcher=lambda react, config, prog: defer.succeed(config))
        ep = serverFromString(self.reactor, 'onion:88:localPort=1234:hiddenServiceDir=/foo/bar')
        self.assertEqual(ep.public_port, 88)
        self.assertEqual(ep.local_port, 1234)
        self.assertEqual(ep.hidden_service_dir, '/foo/bar')


class EndpointLaunchTests(unittest.TestCase):

    def setUp(self):
        self.reactor = FakeReactorTcp(self)
        self.protocol = FakeControlProtocol([])

    def test_onion_address(self):
        addr = TorOnionAddress("foo.onion", 80)
        # just want to run these and assure they don't throw
        # exceptions.
        repr(addr)
        hash(addr)

    def test_onion_parse_unix_socket(self):
        r = Mock()
        ep = serverFromString(r, "onion:80:controlPort=/tmp/foo")


    @patch('txtorcon.TCPHiddenServiceEndpoint.system_tor')
    @patch('txtorcon.TCPHiddenServiceEndpoint.global_tor')
    @patch('txtorcon.TCPHiddenServiceEndpoint.private_tor')
    @defer.inlineCallbacks
    def test_endpoint_launch_tor(self, private_tor, global_tor, system_tor):
        """
        we just want to confirm that calling listen results in the
        spawning of a Tor process; the parsing/setup from string are
        checked elsewhere.
        """

        reactor = proto_helpers.MemoryReactor()
        ep = serverFromString(reactor, 'onion:8888')
        r = yield ep.listen(NoOpProtocolFactory())
        self.assertEqual(global_tor.call_count, 1)
        self.assertEqual(private_tor.call_count, 0)
        self.assertEqual(system_tor.call_count, 0)

    @patch('txtorcon.TCPHiddenServiceEndpoint.system_tor')
    @patch('txtorcon.TCPHiddenServiceEndpoint.global_tor')
    @patch('txtorcon.TCPHiddenServiceEndpoint.private_tor')
    @defer.inlineCallbacks
    def test_endpoint_connect_tor(self, private_tor, global_tor, system_tor):
        """
        similar to above test, we're confirming that an
        endpoint-string with 'controlPort=xxxx' in it calls the API
        that will connect to a running Tor.
        """

        reactor = proto_helpers.MemoryReactor()
        ep = serverFromString(reactor, 'onion:8888:controlPort=9055:localPort=1234')
        r = yield ep.listen(NoOpProtocolFactory())
        self.assertEqual(global_tor.call_count, 0)
        self.assertEqual(private_tor.call_count, 0)
        self.assertEqual(system_tor.call_count, 1)

        # unfortunately, we don't add the hidden-service
        # configurations until we've connected to the launched Tor
        # and bootstrapped a TorConfig object -- and that's a ton
        # of stuff to fake out. Most of that is covered by the
        # parsing tests (i.e. are we getting the right config
        # values from a server-endpoint-string)

















class FakeProtocol(object):
    implements(IProtocol)

    def dataReceived(self, data):
        print "DATA", data

    def connectionLost(self, reason):
        print "LOST", reason

    def makeConnection(self, transport):
        print "MAKE", transport
        transport.protocol = self

    def connectionMade(self):
        print "MADE!"


class FakeAddress(object):
    implements(IAddress)

    compareAttributes = ('type', 'host', 'port')
    type = 'fakeTCP'

    def __init__(self, host, port):
        self.host = host
        self.port = port

    def __repr__(self):
        return '%s(%r, %d)' % (
            self.__class__.__name__, self.host, self.port)

    def __hash__(self):
        return hash((self.type, self.host, self.port))

class FakeListeningPort(object):
    implements(IListeningPort)

    def __init__(self, port):
        self.port = port

    def startListening(self):
        self.factory.doStart()

    def stopListening(self):
        self.factory.doStop()

    def getHost(self):
        return FakeAddress('host', self.port)

def port_generator():
    for x in xrange(65535, 0, -1):
        yield x


from test_torconfig import FakeReactor # FIXME
from test_torconfig import FakeProcessTransport # FIXME
from test_torconfig import FakeControlProtocol # FIXME
class FakeReactorTcp(FakeReactor):#object):
    implements(IReactorTCP)

    failures = 0
    _port_generator = port_generator()

    def __init__(self, test):
        self.protocol = TorControlProtocol()
        self.protocol.connectionMade = lambda: None
        self.transport = proto_helpers.StringTransport()
        self.transport = FakeProcessTransport()
        self.transport.protocol = self.protocol
        def blam():
            self.protocol.outReceived("Bootstrap")
        self.transport.closeStdin = blam
        self.protocol.makeConnection(self.transport)
        FakeReactor.__init__(self, test, self.transport, lambda x: None)

    def listenTCP(self, port, factory, **kwargs):
        '''returns IListeningPort'''
        if self.failures > 0:
            self.failures -= 1
            raise error.CannotListenError(None, None, None)

        if port == 0:
            port = self._port_generator.next()
        p = FakeListeningPort(port)
        p.factory = factory
        p.startListening()
        return p

    def connectTCP(self, host, port, factory, timeout, bindAddress):
        '''should return IConnector'''
#        print "CONN", host, port, factory
#        print dir(factory)
#        print "XX", factory
        r = tcp.Connector(host, port, factory, timeout, bindAddress, reactor=self)
#        print dir(r)
        def blam(*args):
            print "BLAAAAAM", args
        r.connect = blam
        return r

<commit_msg>remove dead test<commit_after>import os
import shutil
import tempfile

from mock import patch
from mock import Mock

from zope.interface import implements

from twisted.trial import unittest
from twisted.test import proto_helpers
from twisted.internet import defer, error, task, tcp
from twisted.internet.endpoints import TCP4ServerEndpoint, serverFromString, clientFromString
from twisted.python.failure import Failure
from twisted.internet.interfaces import IReactorCore, IProtocolFactory, IProtocol, IReactorTCP, IListeningPort, IAddress

from txtorcon import TorControlProtocol
from txtorcon import ITorControlProtocol
from txtorcon import TorConfig
from txtorcon import launch_tor
from txtorcon import TCPHiddenServiceEndpoint
from txtorcon import TorNotFound
from txtorcon import TCPHiddenServiceEndpointParser
from txtorcon import IProgressProvider
from txtorcon import TorOnionAddress
from txtorcon.endpoints import NoOpProtocolFactory
from txtorcon.endpoints import get_global_tor                       # FIXME


class EndpointTests(unittest.TestCase):

    def setUp(self):
        from txtorcon import endpoints
        endpoints._global_tor_config = None
        del endpoints._global_tor_lock
        endpoints._global_tor_lock = defer.DeferredLock()
        self.reactor = FakeReactorTcp(self)
        self.protocol = FakeControlProtocol([])
        self.config = TorConfig(self.protocol)
        self.protocol.answers.append('config/names=\nHiddenServiceOptions Virtual')
        self.protocol.answers.append('HiddenServiceOptions')

    def tearDown(self):
        from txtorcon import endpoints
        endpoints._global_tor_config = None
        del endpoints._global_tor_lock
        endpoints._global_tor_lock = defer.DeferredLock()

    @defer.inlineCallbacks
    def test_global_tor_error(self):
        config0 = yield get_global_tor(Mock(), _tor_launcher=lambda x, y, z: True)
        # now if we specify a control_port it should be an error since
        # the above should have launched one.
        try:
            config1 = yield get_global_tor(Mock(), control_port=111, _tor_launcher=lambda x, y, z: True)
            self.fail()
        except RuntimeError as e:
            # should be an error
            pass

    @defer.inlineCallbacks
    def test_endpoint_properties(self):
        ep = yield TCPHiddenServiceEndpoint.private_tor(Mock(), 80)
        self.assertEqual(None, ep.onion_private_key)
        self.assertEqual(None, ep.onion_uri)
        ep.hiddenservice = Mock()
        ep.hiddenservice.private_key = 'mumble'
        self.assertEqual('mumble', ep.onion_private_key)

    @defer.inlineCallbacks
    def test_private_tor(self):
        m = Mock()
        from txtorcon import endpoints
        endpoints.launch_tor = m
        ep = yield TCPHiddenServiceEndpoint.private_tor(Mock(), 80, control_port=1234)
        m.assert_called()

    @defer.inlineCallbacks
    def test_private_tor_no_control_port(self):
        m = Mock()
        from txtorcon import endpoints
        endpoints.launch_tor = m
        ep = yield TCPHiddenServiceEndpoint.private_tor(Mock(), 80)
        m.assert_called()

    @defer.inlineCallbacks
    def test_system_tor(self):
        from test_torconfig import FakeControlProtocol
        def boom(*args):
            # why does the new_callable thing need a callable that
            # returns a callable? Feels like I must be doing something
            # wrong somewhere...
            def bam(*args, **kw):
                return self.protocol
            return bam
        with patch('txtorcon.endpoints.launch_tor') as m:
            with patch('txtorcon.endpoints.build_tor_connection', new_callable=boom) as btc:
                ep = yield TCPHiddenServiceEndpoint.system_tor(self.reactor,
                                                           clientFromString(self.reactor, "tcp:localhost:port=9050"), 80)
                port = yield ep.listen(NoOpProtocolFactory())
                toa = port.getHost()
                self.assertTrue(hasattr(toa, 'onion_uri'))
                self.assertTrue(hasattr(toa, 'onion_port'))
                port.startListening()
                str(port)
                port.tor_config
                m.assert_called()
        

    @defer.inlineCallbacks
    def test_basic(self):
        listen = RuntimeError("listen")
        connect = RuntimeError("connect")
        reactor = proto_helpers.RaisingMemoryReactor(listen, connect)
        reactor.addSystemEventTrigger = Mock()

        ep = TCPHiddenServiceEndpoint(reactor, self.config, 123)
        self.config.bootstrap()
        yield self.config.post_bootstrap
        self.assertTrue(IProgressProvider.providedBy(ep))

        try:
            port = yield ep.listen(NoOpProtocolFactory())
            self.fail("Should have been an exception")
        except RuntimeError as e:
            # make sure we called listenTCP not connectTCP
            self.assertEqual(e, listen)

        #self.assertIsInstance(port, torconfig.TorOnionListeningPort)
        #self.assertEqual('127.0.0.1', ep.tcp_endpoint._interface)
        ## make sure _ListWrapper's __repr__ doesn't explode
        repr(self.config.HiddenServices)

    def test_progress_updates(self):
        config = TorConfig()
        ep = TCPHiddenServiceEndpoint(self.reactor, config, 123)

        self.assertTrue(IProgressProvider.providedBy(ep))
        prog = IProgressProvider(ep)
        ding = Mock()
        prog.add_progress_listener(ding)
        args = (50, "blarg", "Doing that thing we talked about.")
        # kind-of cheating, test-wise?
        ep._tor_progress_update(*args)
        ding.assert_called_with(*args)
        

    def test_multiple_listen(self):
        ep = TCPHiddenServiceEndpoint(self.reactor, self.config, 123)
        d0 = ep.listen(NoOpProtocolFactory())

        @defer.inlineCallbacks
        def more_listen(arg):
            yield arg.stopListening()
            d1 = ep.listen(NoOpProtocolFactory())

            def foo(arg):
                return arg
            d1.addBoth(foo)
            defer.returnValue(arg)
            return
        d0.addBoth(more_listen)
        self.config.bootstrap()

        def check(arg):
            self.assertEqual('127.0.0.1', ep.tcp_endpoint._interface)
        d0.addCallback(check).addErrback(self.fail)
        return d0

    def test_already_bootstrapped(self):
        self.config.bootstrap()

        ep = TCPHiddenServiceEndpoint(self.reactor, self.config, 123)
        d = ep.listen(NoOpProtocolFactory())
        return d

    @defer.inlineCallbacks
    def test_explicit_data_dir(self):
        config = TorConfig()
        ep = TCPHiddenServiceEndpoint(self.reactor, config, 123, '/mumble/mumble')

        # fake out some things so we don't actually have to launch + bootstrap
        class FakeTorProcessProtocol(object):
            tor_protocol = self.reactor.protocol
        process = FakeTorProcessProtocol()
        ep._launch_tor = Mock(return_value=process)
        config._update_proto(Mock())
        config.bootstrap()
        yield config.post_bootstrap

        # make sure listen() correctly configures our hidden-serivce
        # with the explicit directory we passed in above
        port = yield ep.listen(NoOpProtocolFactory())
        self.assertEqual(1, len(config.HiddenServices))
        self.assertEqual(config.HiddenServices[0].dir, '/mumble/mumble')

    def test_failure(self):
        self.reactor.failures = 1
        ep = TCPHiddenServiceEndpoint(self.reactor, self.config, 123)
        d = ep.listen(NoOpProtocolFactory())
        self.config.bootstrap()
        d.addErrback(self.check_error)
        return d

    def check_error(self, failure):
        self.assertEqual(failure.type, error.CannotListenError)
        return None

    def test_parse_via_plugin(self):
        # make sure we have a valid thing from get_global_tor without actually launching tor
        config = TorConfig()
        config.post_bootstrap = defer.succeed(config)
        from txtorcon import torconfig
        torconfig._global_tor_config = None
        get_global_tor(self.reactor,
                       _tor_launcher=lambda react, config, prog: defer.succeed(config))
        ep = serverFromString(self.reactor, 'onion:88:localPort=1234:hiddenServiceDir=/foo/bar')
        self.assertEqual(ep.public_port, 88)
        self.assertEqual(ep.local_port, 1234)
        self.assertEqual(ep.hidden_service_dir, '/foo/bar')


class EndpointLaunchTests(unittest.TestCase):

    def setUp(self):
        self.reactor = FakeReactorTcp(self)
        self.protocol = FakeControlProtocol([])

    def test_onion_address(self):
        addr = TorOnionAddress("foo.onion", 80)
        # just want to run these and assure they don't throw
        # exceptions.
        repr(addr)
        hash(addr)

    def test_onion_parse_unix_socket(self):
        r = Mock()
        ep = serverFromString(r, "onion:80:controlPort=/tmp/foo")


    @patch('txtorcon.TCPHiddenServiceEndpoint.system_tor')
    @patch('txtorcon.TCPHiddenServiceEndpoint.global_tor')
    @patch('txtorcon.TCPHiddenServiceEndpoint.private_tor')
    @defer.inlineCallbacks
    def test_endpoint_launch_tor(self, private_tor, global_tor, system_tor):
        """
        we just want to confirm that calling listen results in the
        spawning of a Tor process; the parsing/setup from string are
        checked elsewhere.
        """

        reactor = proto_helpers.MemoryReactor()
        ep = serverFromString(reactor, 'onion:8888')
        r = yield ep.listen(NoOpProtocolFactory())
        self.assertEqual(global_tor.call_count, 1)
        self.assertEqual(private_tor.call_count, 0)
        self.assertEqual(system_tor.call_count, 0)

    @patch('txtorcon.TCPHiddenServiceEndpoint.system_tor')
    @patch('txtorcon.TCPHiddenServiceEndpoint.global_tor')
    @patch('txtorcon.TCPHiddenServiceEndpoint.private_tor')
    @defer.inlineCallbacks
    def test_endpoint_connect_tor(self, private_tor, global_tor, system_tor):
        """
        similar to above test, we're confirming that an
        endpoint-string with 'controlPort=xxxx' in it calls the API
        that will connect to a running Tor.
        """

        reactor = proto_helpers.MemoryReactor()
        ep = serverFromString(reactor, 'onion:8888:controlPort=9055:localPort=1234')
        r = yield ep.listen(NoOpProtocolFactory())
        self.assertEqual(global_tor.call_count, 0)
        self.assertEqual(private_tor.call_count, 0)
        self.assertEqual(system_tor.call_count, 1)

        # unfortunately, we don't add the hidden-service
        # configurations until we've connected to the launched Tor
        # and bootstrapped a TorConfig object -- and that's a ton
        # of stuff to fake out. Most of that is covered by the
        # parsing tests (i.e. are we getting the right config
        # values from a server-endpoint-string)

















class FakeProtocol(object):
    implements(IProtocol)

    def dataReceived(self, data):
        print "DATA", data

    def connectionLost(self, reason):
        print "LOST", reason

    def makeConnection(self, transport):
        print "MAKE", transport
        transport.protocol = self

    def connectionMade(self):
        print "MADE!"


class FakeAddress(object):
    implements(IAddress)

    compareAttributes = ('type', 'host', 'port')
    type = 'fakeTCP'

    def __init__(self, host, port):
        self.host = host
        self.port = port

    def __repr__(self):
        return '%s(%r, %d)' % (
            self.__class__.__name__, self.host, self.port)

    def __hash__(self):
        return hash((self.type, self.host, self.port))

class FakeListeningPort(object):
    implements(IListeningPort)

    def __init__(self, port):
        self.port = port

    def startListening(self):
        self.factory.doStart()

    def stopListening(self):
        self.factory.doStop()

    def getHost(self):
        return FakeAddress('host', self.port)

def port_generator():
    for x in xrange(65535, 0, -1):
        yield x


from test_torconfig import FakeReactor # FIXME
from test_torconfig import FakeProcessTransport # FIXME
from test_torconfig import FakeControlProtocol # FIXME
class FakeReactorTcp(FakeReactor):#object):
    implements(IReactorTCP)

    failures = 0
    _port_generator = port_generator()

    def __init__(self, test):
        self.protocol = TorControlProtocol()
        self.protocol.connectionMade = lambda: None
        self.transport = proto_helpers.StringTransport()
        self.transport = FakeProcessTransport()
        self.transport.protocol = self.protocol
        def blam():
            self.protocol.outReceived("Bootstrap")
        self.transport.closeStdin = blam
        self.protocol.makeConnection(self.transport)
        FakeReactor.__init__(self, test, self.transport, lambda x: None)

    def listenTCP(self, port, factory, **kwargs):
        '''returns IListeningPort'''
        if self.failures > 0:
            self.failures -= 1
            raise error.CannotListenError(None, None, None)

        if port == 0:
            port = self._port_generator.next()
        p = FakeListeningPort(port)
        p.factory = factory
        p.startListening()
        return p

    def connectTCP(self, host, port, factory, timeout, bindAddress):
        '''should return IConnector'''
#        print "CONN", host, port, factory
#        print dir(factory)
#        print "XX", factory
        r = tcp.Connector(host, port, factory, timeout, bindAddress, reactor=self)
#        print dir(r)
        def blam(*args):
            print "BLAAAAAM", args
        r.connect = blam
        return r


{'fraction_non_alphanumeric': 0.08737623762376237, 'fraction_numerical': 0.008875530410183876, 'mean_word_length': 2.6685692048255287, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 5, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 5, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4190436', 'n_tokens_mistral': 8192, 'n_tokens_neox': 7794, 'n_words': 1874}