import sys
import unittest

import gi.overrides
import gi.module
import gi.importer

from gi.repository import Regress


class TestOverrides(unittest.TestCase):
    def test_non_gi(self):
        class MyClass:
            pass

        try:
            gi.overrides.override(MyClass)
            self.fail("unexpected success of overriding non-GI class")
        except TypeError as e:
            self.assertTrue("Can not override a type MyClass" in str(e))

    def test_separate_path(self):
        # Regress override is in tests/gi/overrides, separate from gi/overrides
        # https://bugzilla.gnome.org/show_bug.cgi?id=680913
        self.assertEqual(Regress.REGRESS_OVERRIDE, 42)

    def test_load_overrides(self):
        mod = gi.module.get_introspection_module("GIMarshallingTests")
        mod_override = gi.overrides.load_overrides(mod)
        self.assertTrue(mod_override is not mod)
        self.assertTrue(mod_override._introspection_module is mod)
        self.assertEqual(mod_override.OVERRIDES_CONSTANT, 7)
        self.assertEqual(mod.OVERRIDES_CONSTANT, 42)

    def test_load_no_overrides(self):
        mod_key = "gi.overrides.GIMarshallingTests"
        had_mod = mod_key in sys.modules
        old_mod = sys.modules.get(mod_key)
        try:
            # this makes override import fail
            sys.modules[mod_key] = None
            mod = gi.module.get_introspection_module("GIMarshallingTests")
            mod_override = gi.overrides.load_overrides(mod)
            self.assertTrue(mod_override is mod)
        finally:
            del sys.modules[mod_key]
            if had_mod:
                sys.modules[mod_key] = old_mod


class TestModule(unittest.TestCase):
    # Tests for gi.module

    def test_get_introspection_module_caching(self):
        # This test attempts to minimize side effects by
        # using a DynamicModule directly instead of going though:
        # from gi.repository import Foo

        # Clear out introspection module cache before running this test.
        old_modules = gi.module._introspection_modules
        gi.module._introspection_modules = {}

        mod_name = "GIMarshallingTests"
        mod1 = gi.module.get_introspection_module(mod_name)
        mod2 = gi.module.get_introspection_module(mod_name)
        self.assertTrue(mod1 is mod2)

        # Restore the previous cache
        gi.module._introspection_modules = old_modules

    def test_module_dependency_loading(self):
        # Difficult to because this generally need to run in isolation to make
        # sure GIMarshallingTests has not yet been loaded. But we can do this with:
        #  make check TEST_NAMES=test_import_machinery.TestModule.test_module_dependency_loading
        if "gi.repository.Gio" in sys.modules:
            return

        from gi.repository import GIMarshallingTests

        GIMarshallingTests

        self.assertIn("gi.repository.Gio", sys.modules)
        self.assertIn("gi.repository.GIMarshallingTests", sys.modules)

    def test_static_binding_protection(self):
        # Importing old static bindings once gi has been imported should not
        # crash but instead give back a dummy module which produces RuntimeErrors
        # on access.
        with self.assertRaises(AttributeError):
            import gobject

            gobject.anything

        with self.assertRaises(AttributeError):
            import glib

            glib.anything

        with self.assertRaises(AttributeError):
            import gio

            gio.anything

        with self.assertRaises(AttributeError):
            import gtk

            gtk.anything

        with self.assertRaises(AttributeError):
            import gtk.gdk

            gtk.gdk.anything


class TestImporter(unittest.TestCase):
    def test_invalid_repository_module_name(self):
        with self.assertRaises(ImportError) as context:
            from gi.repository import InvalidGObjectRepositoryModuleName

            InvalidGObjectRepositoryModuleName

        exception_string = str(context.exception)

        self.assertTrue("InvalidGObjectRepositoryModuleName" in exception_string)
        self.assertTrue("introspection typelib" in exception_string)

    def test_require_version_warning(self):
        check = gi.importer._check_require_version

        # make sure it doesn't fail at least
        with check("GLib", 1):
            from gi.repository import GLib

            GLib

        # make sure the exception propagates
        with (
            self.assertRaises(ImportError),
            check("InvalidGObjectRepositoryModuleName", 1),
        ):
            from gi.repository import InvalidGObjectRepositoryModuleName

            InvalidGObjectRepositoryModuleName

    def test_require_version_versiontype(self):
        import gi

        with self.assertRaises(ValueError):
            gi.require_version("GLib", 2.0)

        with self.assertRaises(ValueError):
            gi.require_version("GLib", b"2.0")

    def test_require_versions(self):
        import gi

        gi.require_versions({"GLib": "2.0", "Gio": "2.0", "GObject": "2.0"})
        from gi.repository import GLib

        GLib

    def test_get_import_stacklevel(self):
        gi.importer.get_import_stacklevel(import_hook=True)
        gi.importer.get_import_stacklevel(import_hook=False)
