Repository navigation
Expand file tree
/
Copy pathtest_paquet_contenu.py
More file actions
135 lines (112 loc) · 5.52 KB
/
Copy pathtest_paquet_contenu.py
File metadata and controls
135 lines (112 loc) · 5.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
"""Contenu des paquets .deb/.rpm (issue #162).
``make install DESTDIR=...`` est la seule étape commune à ``make deb``,
``make rpm`` et ``make opensuse`` (fpm emballe ensuite ce répertoire tel
quel). Avant l'issue #162, seuls ``gnome_connection_manager.py``,
``pyAES.py`` et ``urlregex.py`` étaient copiés : l'application installée
échouait dès l'import de ``gcm4_core`` et n'avait aucun plugin.
Ce test lance la vraie cible (msgfmt requis : installé dans le job Qualité)
et vérifie que chaque module local importé par un fichier installé, y
compris par un import différé dans une fonction, est lui aussi installé.
"""
from __future__ import annotations
import ast
import re
import subprocess
import tempfile
import unittest
import warnings
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
APP = Path("usr/share/gnome-connection-manager")
LOCAL_ROOTS = {p.stem for p in ROOT.glob("*.py")} | {"plugins"}
def _imports(path: Path) -> set[str]:
with warnings.catch_warnings():
warnings.simplefilter("ignore", SyntaxWarning)
tree = ast.parse(path.read_text(encoding="utf-8"))
found: set[str] = set()
for node in ast.walk(tree):
if isinstance(node, ast.Import):
found.update(a.name for a in node.names)
elif isinstance(node, ast.ImportFrom) and node.module and node.level == 0:
found.add(node.module)
found.update(f"{node.module}.{a.name}" for a in node.names)
return {m for m in found if m.split(".")[0] in LOCAL_ROOTS}
def _resolu(app: Path, module: str) -> bool:
"""Le module (ou, pour ``from x import nom``, son paquet parent) est installé."""
candidats = [module, module.rsplit(".", 1)[0]] if "." in module else [module]
for nom in candidats:
base = app / Path(*nom.split("."))
if base.with_suffix(".py").is_file() or (base / "__init__.py").is_file():
return True
if base.is_dir() and nom == "plugins":
return True # plugins/ : répertoire chargé par plugin_base (pas un paquet)
return False
class TestContenuDuPaquet(unittest.TestCase):
"""Arborescence produite par ``make install``."""
@classmethod
def setUpClass(cls):
"""Installe une fois dans un répertoire temporaire."""
cls._tmp = tempfile.TemporaryDirectory()
dest = Path(cls._tmp.name)
result = subprocess.run(
["make", "-s", "install", f"DESTDIR={dest}"], cwd=ROOT, capture_output=True, text=True
)
if result.returncode != 0:
raise AssertionError(f"make install a échoué :\n{result.stdout}\n{result.stderr}")
cls.app = dest / APP
cls.dest = dest
@classmethod
def tearDownClass(cls):
"""Supprime l'installation temporaire."""
cls._tmp.cleanup()
def test_point_d_entree_executable(self):
"""Le .desktop lance gnome_connection_manager.py : présent et exécutable."""
entree = self.app / "gnome_connection_manager.py"
self.assertTrue(entree.is_file())
self.assertTrue(entree.stat().st_mode & 0o111)
desktop = (
self.dest / "usr/share/applications/gnome-connection-manager.desktop"
).read_text(encoding="utf-8")
self.assertIn(f"Exec=/{APP}/gnome_connection_manager.py", desktop)
def test_tous_les_modules_de_la_racine(self):
"""Chaque .py de la racine du dépôt est installé."""
manquants = sorted(p.name for p in ROOT.glob("*.py") if not (self.app / p.name).is_file())
self.assertEqual(manquants, [])
def test_plugins_installes_sans_tests(self):
"""Les plugins sont là, leurs tests non."""
self.assertTrue((self.app / "plugins" / "plugin_base.py").is_file())
self.assertTrue((self.app / "plugins" / "ssh" / "core.py").is_file())
attendus = {p.name for p in (ROOT / "plugins").glob("plugin_*.py")}
self.assertEqual({p.name for p in (self.app / "plugins").glob("plugin_*.py")}, attendus)
self.assertEqual(
[p for p in self.app.rglob("*") if "tests" in p.parts or p.name == "conftest.py"], []
)
def test_chaque_import_local_est_installe(self):
"""Aucun ImportError à prévoir : tout module local importé est embarqué."""
manquants = {}
for fichier in sorted(self.app.rglob("*.py")):
absents = sorted(m for m in _imports(fichier) if not _resolu(self.app, m))
if absents:
manquants[str(fichier.relative_to(self.app))] = absents
self.assertEqual(manquants, {})
def test_traductions_compilees(self):
"""Les .mo des langues de lang/LINGUAS sont présents."""
langues = [
ligne.strip()
for ligne in (ROOT / "lang" / "LINGUAS").read_text(encoding="utf-8").splitlines()
if ligne.strip() and not ligne.startswith("#")
]
self.assertTrue(langues)
mo = list((self.app / "lang").rglob("*.mo"))
self.assertGreaterEqual(len(mo), len(langues))
class TestDependancesDesPaquets(unittest.TestCase):
"""Dépendances déclarées à fpm."""
def test_loguru_declaree_partout(self):
"""logging_config importe loguru au démarrage : dépendance de chaque paquet."""
makefile = (ROOT / "Makefile").read_text(encoding="utf-8")
for cible in ("deb", "rpm", "opensuse"):
bloc = re.search(rf"^{cible}:\n(.*?)(?=^\S)", makefile, re.S | re.M)
self.assertIsNotNone(bloc, cible)
self.assertIn("-d python3-loguru", bloc.group(1), cible)
if __name__ == "__main__":
unittest.main()