import __future__

import os

import unittest

import distutils.dir_util

import tempfile



from test import test_support



try: set

except NameError: from sets import Set as set



import modulefinder



# Note: To test modulefinder with Python 2.2, sets.py and

# modulefinder.py must be available - they are not in the standard

# library.



TEST_DIR = tempfile.mkdtemp()

TEST_PATH = [TEST_DIR, os.path.dirname(__future__.__file__)]



# Each test description is a list of 5 items:

#

# 1. a module name that will be imported by modulefinder

# 2. a list of module names that modulefinder is required to find

# 3. a list of module names that modulefinder should complain

#    about because they are not found

# 4. a list of module names that modulefinder should complain

#    about because they MAY be not found

# 5. a string specifying packages to create; the format is obvious imo.

#

# Each package will be created in TEST_DIR, and TEST_DIR will be

# removed after the tests again.

# Modulefinder searches in a path that contains TEST_DIR, plus

# the standard Lib directory.



maybe_test = [

    "a.module",

    ["a", "a.module", "sys",

     "b"],

    ["c"], ["b.something"],

    """\

a/__init__.py

a/module.py

                                from b import something

                                from c import something

b/__init__.py

                                from sys import *

"""]



maybe_test_new = [

    "a.module",

    ["a", "a.module", "sys",

     "b", "__future__"],

    ["c"], ["b.something"],

    """\

a/__init__.py

a/module.py

                                from b import something

                                from c import something

b/__init__.py

                                from __future__ import absolute_import

                                from sys import *

"""]



package_test = [

    "a.module",

    ["a", "a.b", "a.c", "a.module", "mymodule", "sys"],

    ["blahblah"], [],

    """\

mymodule.py

a/__init__.py

                                import blahblah

                                from a import b

                                import c

a/module.py

                                import sys

                                from a import b as x

                                from a.c import sillyname

a/b.py

a/c.py

                                from a.module import x

                                import mymodule as sillyname

                                from sys import version_info

"""]



absolute_import_test = [

    "a.module",

    ["a", "a.module",

     "b", "b.x", "b.y", "b.z",

     "__future__", "sys", "exceptions"],

    ["blahblah"], [],

    """\

mymodule.py

a/__init__.py

a/module.py

                                from __future__ import absolute_import

                                import sys # sys

                                import blahblah # fails

                                import exceptions # exceptions

                                import b.x # b.x

                                from b import y # b.y

                                from b.z import * # b.z.*

a/exceptions.py

a/sys.py

                                import mymodule

a/b/__init__.py

a/b/x.py

a/b/y.py

a/b/z.py

b/__init__.py

                                import z

b/unused.py

b/x.py

b/y.py

b/z.py

"""]



relative_import_test = [

    "a.module",

    ["__future__",

     "a", "a.module",

     "a.b", "a.b.y", "a.b.z",

     "a.b.c", "a.b.c.moduleC",

     "a.b.c.d", "a.b.c.e",

     "a.b.x",

     "exceptions"],

    [], [],

    """\

mymodule.py

a/__init__.py

                                from .b import y, z # a.b.y, a.b.z

a/module.py

                                from __future__ import absolute_import # __future__

                                import exceptions # exceptions

a/exceptions.py

a/sys.py

a/b/__init__.py

                                from ..b import x # a.b.x

                                #from a.b.c import moduleC

                                from .c import moduleC # a.b.moduleC

a/b/x.py

a/b/y.py

a/b/z.py

a/b/g.py

a/b/c/__init__.py

                                from ..c import e # a.b.c.e

a/b/c/moduleC.py

                                from ..c import d # a.b.c.d

a/b/c/d.py

a/b/c/e.py

a/b/c/x.py

"""]



relative_import_test_2 = [

    "a.module",

    ["a", "a.module",

     "a.sys",

     "a.b", "a.b.y", "a.b.z",

     "a.b.c", "a.b.c.d",

     "a.b.c.e",

     "a.b.c.moduleC",

     "a.b.c.f",

     "a.b.x",

     "a.another"],

    [], [],

    """\

mymodule.py

a/__init__.py

                                from . import sys # a.sys

a/another.py

a/module.py

                                from .b import y, z # a.b.y, a.b.z

a/exceptions.py

a/sys.py

a/b/__init__.py

                                from .c import moduleC # a.b.c.moduleC

                                from .c import d # a.b.c.d

a/b/x.py

a/b/y.py

a/b/z.py

a/b/c/__init__.py

                                from . import e # a.b.c.e

a/b/c/moduleC.py

                                #

                                from . import f   # a.b.c.f

                                from .. import x  # a.b.x

                                from ... import another # a.another

a/b/c/d.py

a/b/c/e.py

a/b/c/f.py

"""]



relative_import_test_3 = [

    "a.module",

    ["a", "a.module"],

    ["a.bar"],

    [],

    """\

a/__init__.py

                                def foo(): pass

a/module.py

                                from . import foo

                                from . import bar

"""]



def open_file(path):

    ##print "#", os.path.abspath(path)

    dirname = os.path.dirname(path)

    distutils.dir_util.mkpath(dirname)

    return open(path, "w")



def create_package(source):

    ofi = None

    for line in source.splitlines():

        if line.startswith(" ") or line.startswith("\t"):

            ofi.write(line.strip() + "\n")

        else:

            ofi = open_file(os.path.join(TEST_DIR, line.strip()))



class ModuleFinderTest(unittest.TestCase):

    def _do_test(self, info, report=False):

        import_this, modules, missing, maybe_missing, source = info

        create_package(source)

        try:

            mf = modulefinder.ModuleFinder(path=TEST_PATH)

            mf.import_hook(import_this)

            if report:

                mf.report()

##                # This wouldn't work in general when executed several times:

##                opath = sys.path[:]

##                sys.path = TEST_PATH

##                try:

##                    __import__(import_this)

##                except:

##                    import traceback; traceback.print_exc()

##                sys.path = opath

##                return

            modules = set(modules)

            found = set(mf.modules.keys())

            more = list(found - modules)

            less = list(modules - found)

            # check if we found what we expected, not more, not less

            self.failUnlessEqual((more, less), ([], []))



            # check for missing and maybe missing modules

            bad, maybe = mf.any_missing_maybe()

            self.failUnlessEqual(bad, missing)

            self.failUnlessEqual(maybe, maybe_missing)

        finally:

            distutils.dir_util.remove_tree(TEST_DIR)



    def test_package(self):

        self._do_test(package_test)



    def test_maybe(self):

        self._do_test(maybe_test)



    if getattr(__future__, "absolute_import", None):



        def test_maybe_new(self):

            self._do_test(maybe_test_new)



        def test_absolute_imports(self):

            self._do_test(absolute_import_test)



        def test_relative_imports(self):

            self._do_test(relative_import_test)



        def test_relative_imports_2(self):

            self._do_test(relative_import_test_2)



        def test_relative_imports_3(self):

            self._do_test(relative_import_test_3)



def test_main():

    distutils.log.set_threshold(distutils.log.WARN)

    test_support.run_unittest(ModuleFinderTest)



if __name__ == "__main__":

    unittest.main()

