-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_tools.py
More file actions
149 lines (125 loc) · 5.62 KB
/
test_tools.py
File metadata and controls
149 lines (125 loc) · 5.62 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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import os
import shutil
import subprocess
import sys
from pathlib import Path
import pytest
#region Global Variables
ROOT = Path(__file__).resolve().parent.parent
SCRIPT = ROOT / 'tools.sh'
VENV_TEST = ROOT / '.venv_test'
PACKAGE_NAME = 'stat-log-db'
GITHUB_ACTIONS = os.getenv("GITHUB_ACTIONS") == "true"
#endregion
#region testing tools
def _ensure_test_venv():
"""Ensure the test virtual environment is created."""
if not VENV_TEST.exists():
subprocess.run([sys.executable, '-m', 'venv', str(VENV_TEST)], check=True)
def _venv_python():
"""Return path to the virtual environment's python interpreter."""
return VENV_TEST / ('Scripts' if os.name == 'nt' else 'bin') / ('python.exe' if os.name == 'nt' else 'python')
def is_installed(package: str) -> bool:
"""
Check if a package is installed in the test virtual environment using 'pip show'.
Assumes the test venv has been created.
"""
_ensure_test_venv()
python_executable = _venv_python()
result = subprocess.run([str(python_executable), '-m', 'pip', 'show', package], capture_output=True, text=True)
return result.returncode == 0
def run_tools(args, use_test_venv=False):
"""Run tools.sh returning (code, stdout+stderr)."""
env = os.environ.copy()
if use_test_venv:
_ensure_test_venv()
scripts_dir = VENV_TEST / ('Scripts' if os.name == 'nt' else 'bin')
env['PATH'] = str(scripts_dir) + os.pathsep + env.get('PATH', '')
env['VIRTUAL_ENV'] = str(VENV_TEST)
env['PYTHONHOME'] = '' # ensure venv python resolution
bash = r'C:\Program Files\Git\bin\bash.exe' if os.name == 'nt' else 'bash' # TODO: indicate to the user that they need git bash
proc = subprocess.run([bash, str(SCRIPT), *args], capture_output=True, text=True, cwd=ROOT, env=env)
return proc.returncode, proc.stdout + proc.stderr
#endregion
@pytest.fixture() # scope="module"
def test_venv():
"""
Provision an isolated virtual environment used for install/uninstall tests.
The directory is removed after all related tests complete.
"""
_ensure_test_venv()
yield VENV_TEST
# Teardown: remove the virtual environment directory
if VENV_TEST.exists():
shutil.rmtree(VENV_TEST)
def test_help():
code, out = run_tools(['-h'])
assert code == 0
# Read README.md
readme_path = ROOT / 'README.md'
assert readme_path.exists(), f"README not found at {readme_path}"
readme_content = None
with open(readme_path, 'r', encoding='utf-8') as f:
readme_content = f.read().strip()
assert not (readme_content is None), "Unable to read README"
# Compare README content with help output
try:
assert out == readme_content, "Help output does not match README content"
except AssertionError:
assert out.strip() == readme_content.strip(), "Help output does not match README content (leading & trailing whitespace stripped)"
@pytest.mark.skipif(GITHUB_ACTIONS, reason="Skipping test on GitHub Actions")
def test_install_dev(test_venv):
code, out = run_tools(['-id'], use_test_venv=True)
assert code == 0
assert 'Installing' in out
assert 'dev' in out
assert is_installed(PACKAGE_NAME), 'Package should be installed after dev install'
@pytest.mark.skipif(GITHUB_ACTIONS, reason="Skipping test on GitHub Actions")
def test_install_normal(test_venv):
code, out = run_tools(['-in'], use_test_venv=True)
assert code == 0
assert 'Installing' in out
assert 'dev' not in out
assert is_installed(PACKAGE_NAME), 'Package should be installed after normal install'
def test_install_invalid_arg(test_venv):
code, out = run_tools(['-ix'], use_test_venv=True)
assert code == 1
assert ('Unsupported argument' in out) or ('Invalid install mode' in out)
assert not is_installed(PACKAGE_NAME), 'Package should not be installed after invalid install argument'
@pytest.mark.skipif(GITHUB_ACTIONS, reason="Skipping test on GitHub Actions")
def test_uninstall(test_venv):
# Ensure something installed first (dev mode)
icode, iout = run_tools(['-id'], use_test_venv=True)
assert icode == 0
assert is_installed(PACKAGE_NAME), 'Package should be installed (before uninstall)'
ucode, uout = run_tools(['-u'], use_test_venv=True)
assert ucode == 0
assert 'Uninstalling' in uout
assert 'Uninstall complete' in uout
assert not is_installed(PACKAGE_NAME), 'Package should not be installed after uninstall'
@pytest.mark.skipif(GITHUB_ACTIONS, reason="Skipping test on GitHub Actions")
def test_install_and_clean_multi_flag(test_venv):
code, out = run_tools(['-id', '-c'], use_test_venv=True)
assert code == 0
assert is_installed(PACKAGE_NAME), 'Package should be installed'
assert 'Installing' in out
assert 'Cleaning up workspace' in out
assert 'Cleanup complete' in out
assert is_installed(PACKAGE_NAME), 'Cleanup should not remove installed package'
def test_test_no_arg():
code, out = run_tools(['-t'])
assert code == 1
try:
assert out == 'Option -t requires an argument'
except AssertionError:
assert out.strip() == 'Option -t requires an argument'
def test_test_invalid_arg():
code, out = run_tools(['-tx'])
assert code == 1
assert ('Unsupported argument' in out) or ('Invalid test mode' in out)
@pytest.mark.skipif(GITHUB_ACTIONS, reason="Skipping test on GitHub Actions")
def test_clean():
code, out = run_tools(['-c'])
assert code == 0
assert 'Cleaning up workspace' in out
assert 'Cleanup complete' in out