import sys
from doctest import DocTestCase, DocTestFinder, DocTestParser
from unittest import TestCase, TestSuite, TextTestRunner, TestLoader

class ListTestLoader(TestLoader):
    suiteClass = list

class TestManager(object):
    tests = None
    loader = None

    def __init__(self):
        self.tests = []
        self.loader = ListTestLoader()

    def main(self, test_names = None):
        result = self.run(test_names)
        if not result.wasSuccessful():
            sys.exit(1)
        sys.exit(0)

    def run(self, test_names = None):
        suite = TestSuite()
        runner = TextTestRunner(verbosity = 2)

        for test in self.tests:
            if self.should_run_test(test, test_names):
                suite.addTest(test)

        return runner.run(suite)

    def should_run_test(self, test, test_names):
        if test_names is None:
            return True

        for test_name in test_names:
            test_name_parts = test_name.split('.')
            relevant_id_parts = test.id().split('.')[:len(test_name_parts)]
            if test_name_parts == relevant_id_parts:
                return True

        return False

    def add_test_suite(self, test_suite):
        self.tests.extend(self.flatten_test_suite(test_suite))

    def flatten_test_suite(self, test_suite):
        tests = []
        if isinstance(test_suite, TestSuite):
            for test in list(test_suite):
                tests.extend(self.flatten_test_suite(test))
        else:
            tests.append(test_suite)
        return tests

    def add_test_case_class(self, test_case_class):
        self.tests.extend(
          self.loader.loadTestsFromTestCase(test_case_class))

    def make_doc_test_case(self, test):
        def __init__(self, *args, **kwargs):
            DocTestCase.__init__(self, test)
        return type(
          '%s_TestCase' % test.name.split('.')[-1],
          (DocTestCase,),
          {'__init__': __init__},
        )

    def get_doc_test_cases_from_string(
      self,
      string,
      name = '<string>',
      filename = '<string>',
      globs = None,
    ):
        if globs is None:
            globs = {}

        # Make sure __name__ == '__main__' checks fail:
        globs = dict(globs)
        globs['__name__'] = None

        parser = DocTestParser()
        test = parser.get_doctest(
          string,
          globs = globs,
          name = name,
          filename = filename,
          lineno = 0,
        )
        test_case = self.make_doc_test_case(test)
        return [test_case]

    def add_doc_test_cases_from_string(self, *args, **kwargs):
        for test_case in self.get_doc_test_cases_from_string(*args, **kwargs):
            self.add_test_case_class(test_case)

    def get_doc_test_cases_from_module(self, name):
        from sclapp.util import importName
        mod = importName(name)

        finder = DocTestFinder()
        tests = finder.find(mod)

        doc_test_cases = []
        for test in tests:
            doc_test_cases.append(self.make_doc_test_case(test))
        return doc_test_cases

    def add_doc_test_cases_from_module(self, dst_name, src_name = None):
        if src_name is None:
            src_name = dst_name

        for test_case in self.get_doc_test_cases_from_module(src_name):
            test_case.__module__ = dst_name
            self.add_test_case_class(test_case)

    def get_doc_test_cases_from_text_file(self, filename, *args, **kwargs):
        f = open(filename, 'r')
        try:
            data = f.read()
        finally:
            f.close()

        return self.get_doc_test_cases_from_string(data, *args, **kwargs)

    def add_doc_test_cases_from_text_file(self, *args, **kwargs):
        for test_case in self.get_doc_test_cases_from_text_file(
          *args, **kwargs):
            self.add_test_case_class(test_case)

manager = TestManager()
