# This test module covers support in various parts of the standard library

# for working with modules located inside zipfiles

# The tests are centralised in this fashion to make it easy to drop them

# if a platform doesn't support zipimport

import unittest

import test.test_support

import os

import os.path

import sys

import textwrap

import zipfile

import zipimport

import doctest

import inspect

import linecache

import pdb



verbose = test.test_support.verbose



# Library modules covered by this test set

#  pdb (Issue 4201)

#  inspect (Issue 4223)

#  doctest (Issue 4197)



# Other test modules with zipimport related tests

#  test_zipimport (of course!)

#  test_cmd_line_script (covers the zipimport support in runpy)



# Retrieve some helpers from other test cases

from test import test_doctest, sample_doctest

from test.test_importhooks import ImportHooksBaseTestCase

from test.test_cmd_line_script import temp_dir, _run_python,        \

                                      _spawn_python, _kill_python,  \

                                      _make_test_script,            \

                                      _compile_test_script,         \

                                      _make_test_zip, _make_test_pkg





def _run_object_doctest(obj, module):

    # Direct doctest output (normally just errors) to real stdout; doctest

    # output shouldn't be compared by regrtest.

    save_stdout = sys.stdout

    sys.stdout = test.test_support.get_original_stdout()

    try:

        finder = doctest.DocTestFinder(verbose=verbose, recurse=False)

        runner = doctest.DocTestRunner(verbose=verbose)

        # Use the object's fully qualified name if it has one

        # Otherwise, use the module's name

        try:

            name = "%s.%s" % (obj.__module__, obj.__name__)

        except AttributeError:

            name = module.__name__

        for example in finder.find(obj, name, module):

            runner.run(example)

        f, t = runner.failures, runner.tries

        if f:

            raise test.test_support.TestFailed("%d of %d doctests failed" % (f, t))

    finally:

        sys.stdout = save_stdout

    if verbose:

        print 'doctest (%s) ... %d tests with zero failures' % (module.__name__, t)

    return f, t







class ZipSupportTests(ImportHooksBaseTestCase):

    # We use the ImportHooksBaseTestCase to restore

    # the state of the import related information

    # in the sys module after each test

    # We also clear the linecache and zipimport cache

    # just to avoid any bogus errors due to name reuse in the tests

    def setUp(self):

        linecache.clearcache()

        zipimport._zip_directory_cache.clear()

        ImportHooksBaseTestCase.setUp(self)





    def test_inspect_getsource_issue4223(self):

        test_src = "def foo(): pass\n"

        with temp_dir() as d:

            init_name = _make_test_script(d, '__init__', test_src)

            name_in_zip = os.path.join('zip_pkg',

                                       os.path.basename(init_name))

            zip_name, run_name = _make_test_zip(d, 'test_zip',

                                                init_name, name_in_zip)

            os.remove(init_name)

            sys.path.insert(0, zip_name)

            import zip_pkg

            self.assertEqual(inspect.getsource(zip_pkg.foo), test_src)



    def test_doctest_issue4197(self):

        # To avoid having to keep two copies of the doctest module's

        # unit tests in sync, this test works by taking the source of

        # test_doctest itself, rewriting it a bit to cope with a new

        # location, and then throwing it in a zip file to make sure

        # everything still works correctly

        test_src = inspect.getsource(test_doctest)

        test_src = test_src.replace(

                         "from test import test_doctest",

                         "import test_zipped_doctest as test_doctest")

        test_src = test_src.replace("test.test_doctest",

                                    "test_zipped_doctest")

        test_src = test_src.replace("test.sample_doctest",

                                    "sample_zipped_doctest")

        sample_src = inspect.getsource(sample_doctest)

        sample_src = sample_src.replace("test.test_doctest",

                                        "test_zipped_doctest")

        with temp_dir() as d:

            script_name = _make_test_script(d, 'test_zipped_doctest',

                                            test_src)

            zip_name, run_name = _make_test_zip(d, 'test_zip',

                                                script_name)

            z = zipfile.ZipFile(zip_name, 'a')

            z.writestr("sample_zipped_doctest.py", sample_src)

            z.close()

            if verbose:

                zip_file = zipfile.ZipFile(zip_name, 'r')

                print 'Contents of %r:' % zip_name

                zip_file.printdir()

                zip_file.close()

            os.remove(script_name)

            sys.path.insert(0, zip_name)

            import test_zipped_doctest

            # Some of the doc tests depend on the colocated text files

            # which aren't available to the zipped version (the doctest

            # module currently requires real filenames for non-embedded

            # tests). So we're forced to be selective about which tests

            # to run.

            # doctest could really use some APIs which take a text

            # string or a file object instead of a filename...

            known_good_tests = [

                test_zipped_doctest.SampleClass,

                test_zipped_doctest.SampleClass.NestedClass,

                test_zipped_doctest.SampleClass.NestedClass.__init__,

                test_zipped_doctest.SampleClass.__init__,

                test_zipped_doctest.SampleClass.a_classmethod,

                test_zipped_doctest.SampleClass.a_property,

                test_zipped_doctest.SampleClass.a_staticmethod,

                test_zipped_doctest.SampleClass.double,

                test_zipped_doctest.SampleClass.get,

                test_zipped_doctest.SampleNewStyleClass,

                test_zipped_doctest.SampleNewStyleClass.__init__,

                test_zipped_doctest.SampleNewStyleClass.double,

                test_zipped_doctest.SampleNewStyleClass.get,

                test_zipped_doctest.old_test1,

                test_zipped_doctest.old_test2,

                test_zipped_doctest.old_test3,

                test_zipped_doctest.old_test4,

                test_zipped_doctest.sample_func,

                test_zipped_doctest.test_DocTest,

                test_zipped_doctest.test_DocTestParser,

                test_zipped_doctest.test_DocTestRunner.basics,

                test_zipped_doctest.test_DocTestRunner.exceptions,

                test_zipped_doctest.test_DocTestRunner.option_directives,

                test_zipped_doctest.test_DocTestRunner.optionflags,

                test_zipped_doctest.test_DocTestRunner.verbose_flag,

                test_zipped_doctest.test_Example,

                test_zipped_doctest.test_debug,

                test_zipped_doctest.test_pdb_set_trace,

                test_zipped_doctest.test_pdb_set_trace_nested,

                test_zipped_doctest.test_testsource,

                test_zipped_doctest.test_trailing_space_in_test,

                test_zipped_doctest.test_DocTestSuite,

                test_zipped_doctest.test_DocTestFinder,

            ]

            # These remaining tests are the ones which need access

            # to the data files, so we don't run them

            fail_due_to_missing_data_files = [

                test_zipped_doctest.test_DocFileSuite,

                test_zipped_doctest.test_testfile,

                test_zipped_doctest.test_unittest_reportflags,

            ]

            for obj in known_good_tests:

                _run_object_doctest(obj, test_zipped_doctest)



    def test_doctest_main_issue4197(self):

        test_src = textwrap.dedent("""\

                    class Test:

                        ">>> 'line 2'"

                        pass



                    import doctest

                    doctest.testmod()

                    """)

        pattern = 'File "%s", line 2, in %s'

        with temp_dir() as d:

            script_name = _make_test_script(d, 'script', test_src)

            exit_code, data = _run_python(script_name)

            expected = pattern % (script_name, "__main__.Test")

            if verbose:

                print "Expected line", expected

                print "Got stdout:"

                print data

            self.assert_(expected in data)

            zip_name, run_name = _make_test_zip(d, "test_zip",

                                                script_name, '__main__.py')

            exit_code, data = _run_python(zip_name)

            expected = pattern % (run_name, "__main__.Test")

            if verbose:

                print "Expected line", expected

                print "Got stdout:"

                print data

            self.assert_(expected in data)



    def test_pdb_issue4201(self):

        test_src = textwrap.dedent("""\

                    def f():

                        pass



                    import pdb

                    pdb.runcall(f)

                    """)

        with temp_dir() as d:

            script_name = _make_test_script(d, 'script', test_src)

            p = _spawn_python(script_name)

            p.stdin.write('l\n')

            data = _kill_python(p)

            self.assert_(script_name in data)

            zip_name, run_name = _make_test_zip(d, "test_zip",

                                                script_name, '__main__.py')

            p = _spawn_python(zip_name)

            p.stdin.write('l\n')

            data = _kill_python(p)

            self.assert_(run_name in data)





def test_main():

    test.test_support.run_unittest(ZipSupportTests)

    test.test_support.reap_children()



if __name__ == '__main__':

    test_main()

