78 lines
3.0 KiB
Python
78 lines
3.0 KiB
Python
"""Run the actual installer functions against a real disposable Python venv."""
|
|
import json
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
SOURCE = Path(__file__).resolve().parents[1] / 'scripts/install.ps1'
|
|
PS = shutil.which('powershell') or shutil.which('pwsh')
|
|
pytestmark = pytest.mark.skipif(os.name != 'nt' or not PS, reason='Windows installer runtime test')
|
|
|
|
|
|
def function(source, name):
|
|
start = source.index('function ' + name + ' {')
|
|
opening = source.index('{', start)
|
|
depth = 0
|
|
for index in range(opening, len(source)):
|
|
if source[index] == '{': depth += 1
|
|
elif source[index] == '}': depth -= 1
|
|
if depth == 0: return source[start:index + 1]
|
|
raise AssertionError(name)
|
|
|
|
|
|
@pytest.fixture(scope='module')
|
|
def runtime(tmp_path_factory):
|
|
root = tmp_path_factory.mktemp('installer-reuse')
|
|
subprocess.run([sys.executable, '-m', 'venv', '--without-pip', str(root / 'venv')], check=True)
|
|
return root
|
|
|
|
|
|
def run(runtime, body):
|
|
text = SOURCE.read_text(encoding='utf-8-sig')
|
|
script = runtime / 'probe.ps1'
|
|
prefix = "$ErrorActionPreference = 'Stop'\n"
|
|
prefix += function(text, 'Test-ReusableManagedVenv') + '\n'
|
|
prefix += function(text, 'Install-Venv') + '\n'
|
|
prefix += "$InstallDir = '" + str(runtime).replace("'", "''") + "'\n"
|
|
prefix += "$BasePython = '" + sys._base_executable.replace("'", "''") + "'\n"
|
|
prefix += "$Version = '" + str(sys.version_info.major) + '.' + str(sys.version_info.minor) + "'\n"
|
|
script.write_text(prefix + body, encoding='utf-8-sig')
|
|
result = subprocess.run([PS, '-NoProfile', '-File', str(script)], capture_output=True, text=True, timeout=40)
|
|
assert result.returncode == 0, result.stdout + result.stderr
|
|
return result.stdout
|
|
|
|
|
|
def test_healthy_update_keeps_live_python_and_never_reaches_kill_sweep(runtime):
|
|
python = runtime / 'venv/Scripts/python.exe'
|
|
live = subprocess.Popen([str(python), '-c', 'import time;time.sleep(90)'])
|
|
try:
|
|
output = run(runtime, r'''
|
|
function Resolve-AvailablePythonVersion { return @{Path=$BasePython;Version=$Version} }
|
|
function Write-Success { param($Message); Write-Output $Message }
|
|
function Write-Info { throw 'Unexpected venv recreation/kill path' }
|
|
function taskkill { throw 'Live process must not be killed' }
|
|
Install-Venv
|
|
''')
|
|
assert 'Reusing healthy' in output
|
|
assert live.poll() is None
|
|
assert python.is_file()
|
|
finally:
|
|
live.terminate();live.wait(timeout=10)
|
|
|
|
|
|
def test_wrong_version_or_foreign_interpreter_does_not_reuse(runtime):
|
|
output = run(runtime, r'''
|
|
$venvPath = Join-Path $InstallDir 'venv'
|
|
$results = @(
|
|
(Test-ReusableManagedVenv $venvPath $BasePython '0.0'),
|
|
(Test-ReusableManagedVenv $venvPath (Join-Path $InstallDir 'other-python.exe') $Version),
|
|
(Test-ReusableManagedVenv (Join-Path $InstallDir 'missing') $BasePython $Version)
|
|
)
|
|
ConvertTo-Json -Compress -InputObject $results
|
|
''')
|
|
assert json.loads(output) == [False, False, False]
|