#!/usr/bin/env python
#
# Author: Guillermo Gonzalez <guillermo.gonzalez@canonical.com>
#
# Copyright 2009-2010 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE.  See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program.  If not, see <http://www.gnu.org/licenses/>.

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 unittests 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=W0612
        for root, dirs, files in os.walk("ubuntu_sso"):
            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)
