#!/usr/bin/env python

import os
import re
import signal
import sys
import string
import subprocess
import unittest

sys.path.insert(0, os.path.abspath("."))

from distutils.spawn import find_executable


class TestRunner(object):

    def _load_unittest(self, relpath):
        """Load unit tests from a Python module with the given relative path."""
        assert relpath.endswith(".py"), (
            "%s does not appear to be a Python module" % relpath)
        modpath = relpath.replace(os.path.sep, ".")[:-3]
        module = __import__(modpath, None, None, [""])

        # If the module has a 'suite' or 'test_suite' function, use that
        # to load the tests.
        if hasattr(module, "suite"):
            return module.suite()
        elif hasattr(module, "test_suite"):
            return module.test_suite()
        else:
            return unittest.defaultTestLoader.loadTestsFromModule(module)

    def _collect_tests(self, testpath, test_pattern):
        """Return the set of unittests."""
        suite = unittest.TestSuite()
        if test_pattern:
            pattern = re.compile('.*%s.*' % test_pattern)
        else:
            pattern = None 

        if testpath:
            module_suite = self._load_unittest(testpath)
            if pattern:
                for inner_suite in module_suite._tests:
                    for test in inner_suite._tests:
                        if pattern.match(test.id()):
                            suite.addTest(test)
            else:
                suite.addTests(module_suite)
            return suite

        # We don't use the dirs variable, so ignore the warning
        # pylint: disable-msg=W0612
        for root, dirs, files in os.walk("tests"):
            for file in files:
                path = os.path.join(root, file)
                if file.endswith(".py") and file.startswith("test_"):
                    module_suite = self._load_unittest(path)
                    if pattern:
                        for inner_suite in module_suite._tests:
                            for test in inner_suite._tests:
                                if pattern.match(test.id()):
                                    suite.addTest(test)
                    else:
                        suite.addTests(module_suite)
        return suite

    def run(self, testpath, test_pattern=None, loops=None):
        """run the tests. """
        # install the glib2reactor before any import of the reactor to avoid
        # using the default SelectReactor and be able to run the dbus tests
        from twisted.internet import glib2reactor
        glib2reactor.install()
        from twisted.internet import reactor
        from twisted.trial.reporter import TreeReporter
        from twisted.trial.runner import TrialRunner

        from contrib.dbus_util import DBusRunner
        dbus_runner = DBusRunner()
        dbus_runner.startDBus()

        workingDirectory = os.path.join(os.getcwd(), "_trial_temp", "tmp")
        runner = TrialRunner(reporterFactory=TreeReporter, realTimeErrors=True,
                            workingDirectory=workingDirectory)

        # setup a custom XDG_CACHE_HOME and create the logs directory
        xdg_cache = os.path.join(os.getcwd(), "_trial_temp", "xdg_cache")
        os.environ["XDG_CACHE_HOME"] = xdg_cache
        # setup the ROOTDIR env var
        os.environ['ROOTDIR'] = os.getcwd()
        if not os.path.exists(xdg_cache):
            os.makedirs(xdg_cache)
        success = 0
        try:
            suite = self._collect_tests(testpath, test_pattern)
            if loops:
                old_suite = suite
                suite = unittest.TestSuite()
                for x in xrange(loops):
                    suite.addTest(old_suite)
            result = runner.run(suite)
            success = result.wasSuccessful()
        finally:
            dbus_runner.stopDBus()
        if not success:
            sys.exit(1)
        else:
            sys.exit(0)


if __name__ == '__main__':
    from optparse import OptionParser
    usage = '%prog [options] path'
    parser = OptionParser(usage=usage)
    parser.add_option("-t", "--test", dest="test",
                  help = "run specific tests, e.g: className.methodName")
    parser.add_option("-l", "--loop", dest="loops", type="int", default=1,
                      help = "loop selected tests LOOPS number of times", 
                      metavar="LOOPS")

    (options, args) = parser.parse_args()
    if args:
        testpath = args[0]
        if not os.path.exists(testpath):
            print "the path to test does not exists!"
            sys.exit()
    else:
        testpath = None
    TestRunner().run(testpath, options.test, options.loops) 
