#!/usr/bin/env python

import os
import sys
import socket
import time
import thread
import threading
import unittest

from test import test_support

# Global vars
__addr__ = 'localhost:8002'
script = 'files/thread_script.py'
MAXTRIES = 100

sys.path.append("..")
from mpdb import MPdb, pdbserver, target
from mconnection import (MConnectionClientTCP, MConnectionServerTCP,
                         ConnectionFailed)
from support import MPdbTest, Pdbserver, MPdbTestThread
TESTFN = 'tester'

# This provides us with a fine-grain way of connecting to a server
def connect_to_target(client, address=None):
    if address is None:
        address = __addr__
    client.connection = MConnectionClientTCP()
    
    for i in range(MAXTRIES):
        try:
            client.connection.connect(address)
            if client.connection.connected: break
        except ConnectionFailed, e:
            # This is the error message we expect i.e. when the server thread
            # hasn't started yet. Otherwise it's probably a _real_ error
            if 'Connection refused' in e: pass
            else: raise ConnectionFailed, e
        break
            
class TestRemoteDebugging(unittest.TestCase):
    """ Test Case to make sure debugging remotely works properly. """
    def tearDown(self):
        if TESTFN in os.listdir('.'):
            os.unlink(TESTFN)

# Whilst a lot of the tests below seem to duplicate tests from
# test_mconnection, we need to make sure that the methods that mpdb provides
# are not having side effects, and causing tests that should pass to fail
# and vice versa.

    def testPdbserver(self):
        client = MPdbTest(MPdb())
        client.do_target('tcp localhost:8000')
        
        self.server1 = Pdbserver(MPdb())
        self.server1.start()

        while self.server1.connection is None:
            time.sleep(0.1)
        #self.server1.connection.disconnect()
        self.server1.onecmd('quit\n')

        self.server2 = Pdbserver(MPdb(), 'unknown_protocol unknownhost')
        self.server2.start()
        time.sleep(1.0)
        line = self.server2.lines[0]
        self.assertEquals('*** Unknown protocol\n', line)

        client.onecmd('quit\n')
        self.server2.onecmd('quit\n')

    def testTarget(self):
        server = Pdbserver(MPdb())
        server.start()
        while server.connection is None:
            time.sleep(0.1)

        self.client1 = MPdbTest(MPdb())

        self.client2 = MPdbTest(MPdb())
        self.client2.do_target('dlkdlksldkslkd')

        line = self.client2.lines[0]
        self.assertEquals('*** Invalid arguments\n', line)

        # Hope there's nothing running on this port
        self.client2.do_target('tcp localhost:9000')
        line = self.client2.lines[1]
        errmsg = '*** Failed to connect to localhost:9000: (Connection' \
                 + ' refused)\n'
        self.assertEquals(errmsg, line)

        addr = 'serial /dev/hopefullythisdevicenamedoesntexist'
        self.client2.do_target(addr)
        line = self.client2.lines[2]
        errmsg = '*** Failed to connect to ' \
                 + '/dev/hopefullythisdevicenamedoesntexist:' \
                 + ' (No such file or directory)\n'

        self.assertEquals(errmsg, line)

        server.connection.disconnect()

        for i in range(MAXTRIES):
            if server.connection != None: pass
            else: break

    def testRebindOutput(self):
        self.server = MPdb()
        f = open(TESTFN, 'w+')
        self.server._rebind_output(f)
        self.server.msg('some text')
        f.close()

        f = open(TESTFN, 'r')
        line = f.readline()
        f.close()
        self.assertEquals('some text\n', line, 'Could not rebind output')
        
    def testRebindInput(self):
        self.server = MPdb()
        
        f = open(TESTFN, 'w+')
        f.write('help')
        f.close()

        f = open(TESTFN, 'r')
        self.server._rebind_input(f)
        line = self.server.stdin.readline()

        f.close()
        self.assertEquals(line, 'help', 'Could not rebind input.')

    def testRestart(self):
        server = Pdbserver(MPdb())
        server.start()

        self.client1 = MPdbTest(MPdb())
        self.client1.do_target('tcp localhost:8000')

        while 'Failed' in self.client1.lines[0]:
            time.sleep(1.0)
            self.client1.lines = []
            self.client1.do_target('tcp localhost:8000')

        # The server will _NOT_ come back up so don't try to reconnect
        server.onecmd('restart\n')
        self.client1.connection.write('rquit\n')
        server.connection.disconnect()

        for i in range(MAXTRIES):
            if server.connection != None: pass
            else: break


class TestMpdbDoc(unittest.TestCase):
    """ Test the expected output from help commands against actual
    output to ensure that documentation constantly stays the same.
    """
    def setUp(self):
        self.m = MPdbTest(MPdb())

    def tearDown(self):
        del self.m
        
    def testHelpInfo(self):
        self.m.onecmd('help info')

        exp = 'Display information about the current target\n'
        self.assertEquals(self.m.lines[-1], exp)
        

    def testInfoCmd(self):
        self.m.reset()
        self.m.onecmd('info')
        exp = 'target is local\n'
        self.assertEquals(self.m.lines[-1], exp)

    def testShowCmd(self):
        self.m.reset()
        self.m.onecmd('show')

        exp = "target-address is ''.\n"

        self.assertEquals(self.m.lines[-3], exp)

        
def test_main():
    test_support.run_unittest(TestMpdbDoc, TestRemoteDebugging)
    
if __name__ == '__main__':
    test_main()
