1# This program is free software; you can redistribute it and/or modify
2# it under the terms of the GNU General Public License as published by
3# the Free Software Foundation; either version 2 of the License, or
4# (at your option) any later version.
5
6import os
7import importlib
8import sys
9import shutil
10
11from quodlibet.util.modulescanner import ModuleScanner
12from quodlibet.util.importhelper import get_importables, load_dir_modules
13
14from tests import TestCase, mkdtemp
15
16
17class TModuleScanner(TestCase):
18
19    def setUp(self):
20        self.d = mkdtemp("ql-mod")
21        spec = importlib.machinery.ModuleSpec("qlfake", None)
22        sys.modules["qlfake"] = importlib.util.module_from_spec(spec)
23
24    def tearDown(self):
25        del sys.modules["qlfake"]
26        shutil.rmtree(self.d)
27
28    def _create_mod(self, name, package=None):
29        if package is not None:
30            base = os.path.join(self.d, package)
31        else:
32            base = self.d
33        return open(os.path.join(base, name), "wb")
34
35    def _create_pkg(self, name):
36        base = os.path.join(self.d, name)
37        os.mkdir(base)
38        return open(os.path.join(base, "__init__.py"), "wb")
39
40    def test_importables(self):
41        self.failUnlessEqual(list(get_importables(self.d)), [])
42        h = self._create_mod("foo.py")
43        h.close()
44        self.failUnlessEqual(list(get_importables(self.d))[0],
45                             ("foo", h.name, [h.name]))
46
47    def test_importables_ignore_init(self):
48        h = self._create_mod("foo7.py")
49        h.close()
50        self._create_mod("__init__.py").close()
51        self.failUnlessEqual(list(get_importables(self.d))[0],
52                             ("foo7", h.name, [h.name]))
53
54    def test_importables_package(self):
55        h = self._create_pkg("foobar")
56        self.failUnlessEqual(list(get_importables(self.d))[0],
57                             ("foobar", os.path.dirname(h.name), [h.name]))
58        h.close()
59
60    def test_importables_package_deps(self):
61        h = self._create_pkg("foobar3")
62        h2 = self._create_mod("sub.py", "foobar3")
63        name, path, deps = list(get_importables(self.d))[0]
64        self.failUnlessEqual(name, "foobar3")
65        self.failUnlessEqual(path, os.path.dirname(h.name))
66        self.failUnlessEqual(set(deps), {h.name, h2.name})
67        h2.close()
68        h.close()
69
70    def test_load_dir_modules(self):
71        h = self._create_mod("x.py")
72        h.write(b"test=42\n")
73        h.close()
74        mods = load_dir_modules(self.d, "qlfake")
75        self.failUnlessEqual(len(mods), 1)
76        self.failUnlessEqual(mods[0].test, 42)
77
78    def test_load_dir_modules_packages(self):
79        h = self._create_pkg("somepkg2")
80        h2 = self._create_mod("sub.py", "somepkg2")
81        h2.write(b"test=456\n")
82        h2.close()
83        h.write(b"from .sub import *\nmain=654\n")
84        h.close()
85        mods = load_dir_modules(self.d, "qlfake")
86        self.failUnlessEqual(len(mods), 1)
87        self.failUnlessEqual(mods[0].test, 456)
88
89    def test_scanner_add(self):
90        self._create_mod("q1.py").close()
91        self._create_mod("q2.py").close()
92        s = ModuleScanner([self.d])
93        self.failIf(s.modules)
94        removed, added = s.rescan()
95        self.failIf(removed)
96        self.failUnlessEqual(set(added), {"q1", "q2"})
97        self.failUnlessEqual(len(s.modules), 2)
98        self.failUnlessEqual(len(s.failures), 0)
99
100    def test_unimportable_package(self):
101        self._create_pkg("_foobar").close()
102        s = ModuleScanner([self.d])
103        self.failIf(s.modules)
104        removed, added = s.rescan()
105        self.failIf(added)
106        self.failIf(removed)
107
108    def test_scanner_remove(self):
109        h = self._create_mod("q3.py")
110        h.close()
111        s = ModuleScanner([self.d])
112        s.rescan()
113        os.remove(h.name)
114        try:
115            os.remove(h.name + "c")
116        except OSError:
117            pass
118        removed, added = s.rescan()
119        self.failIf(added)
120        self.failUnlessEqual(removed, ["q3"])
121        self.failUnlessEqual(len(s.modules), 0)
122        self.failUnlessEqual(len(s.failures), 0)
123
124    def test_scanner_error(self):
125        h = self._create_mod("q4.py")
126        h.write(b"1syntaxerror\n")
127        h.close()
128        s = ModuleScanner([self.d])
129        removed, added = s.rescan()
130        self.failIf(added)
131        self.failIf(removed)
132        self.failUnlessEqual(len(s.failures), 1)
133        self.failUnless("q4" in s.failures)
134
135    def test_scanner_add_package(self):
136        h = self._create_pkg("somepkg")
137        h2 = self._create_mod("sub.py", "somepkg")
138        h2.write(b"test=123\n")
139        h2.close()
140        h.write(b"from .sub import *\nmain=321\n")
141        h.close()
142        s = ModuleScanner([self.d])
143        removed, added = s.rescan()
144        self.failUnlessEqual(added, ["somepkg"])
145        self.failUnlessEqual(s.modules["somepkg"].module.main, 321)
146        self.failUnlessEqual(s.modules["somepkg"].module.test, 123)
147