mirror of
https://github.com/miurahr/aqtinstall.git
synced 2025-12-16 20:27:05 +03:00
401 lines
14 KiB
Python
401 lines
14 KiB
Python
import os
|
|
import shutil
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Dict, List, Optional
|
|
|
|
import pytest
|
|
import requests
|
|
|
|
from aqt.commercial import CommercialInstaller, QtPackageInfo, QtPackageManager
|
|
from aqt.exceptions import DiskAccessNotPermitted
|
|
from aqt.helper import Settings, download_installer, get_os_name, get_qt_account_path, get_qt_installer_name
|
|
from aqt.installer import Cli
|
|
from aqt.metadata import Version
|
|
|
|
|
|
class CompletedProcess:
|
|
def __init__(self, args, returncode):
|
|
self.args = args
|
|
self.returncode = returncode
|
|
self.stdout = None
|
|
self.stderr = None
|
|
|
|
|
|
# Test data
|
|
MOCK_XML_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
|
|
<availablepackages>
|
|
<package name="qt.qt6.680.gcc_64" displayname="Desktop gcc" version="6.8.0-0-202312011"/>
|
|
<package name="qt.qt6.680.addons.qtquick3d" displayname="Qt Quick 3D" version="6.8.0-0-202312011"/>
|
|
</availablepackages>"""
|
|
|
|
TEST_EMAIL = os.getenv("AQT_TEST_EMAIL")
|
|
TEST_PASSWORD = os.getenv("AQT_TEST_PASSWORD")
|
|
|
|
|
|
class MockResponse:
|
|
def __init__(self, status_code: int = 200, content: bytes = b"", text: str = "", headers: Dict = None):
|
|
self.status_code = status_code
|
|
self.content = content
|
|
self.text = text
|
|
self.headers = headers or {}
|
|
self.ok = status_code == 200
|
|
|
|
def raise_for_status(self):
|
|
if not self.ok:
|
|
raise requests.HTTPError(f"HTTP Error: {self.status_code}")
|
|
|
|
def iter_content(self, chunk_size=None):
|
|
yield self.content
|
|
|
|
|
|
@pytest.fixture
|
|
def commercial_installer():
|
|
return CommercialInstaller(
|
|
target="desktop",
|
|
arch="gcc_64",
|
|
version="6.8.0",
|
|
username=TEST_EMAIL,
|
|
password=TEST_PASSWORD,
|
|
output_dir="./test_output",
|
|
)
|
|
|
|
|
|
@pytest.mark.enable_socket
|
|
@pytest.mark.parametrize(
|
|
"cmd, expected_arch, expected_err",
|
|
[
|
|
pytest.param(
|
|
"install-qt-official desktop {} 6.8.0",
|
|
{"windows": "win64_msvc2022_64", "linux": "linux_gcc_64", "mac": "clang_64"},
|
|
"No Qt account credentials found",
|
|
),
|
|
],
|
|
)
|
|
def test_cli_login_qt_commercial(capsys, monkeypatch, cmd, expected_arch, expected_err):
|
|
"""Test commercial Qt installation command"""
|
|
# Detect current platform
|
|
current_platform = sys.platform.lower()
|
|
arch = expected_arch[current_platform]
|
|
cmd = cmd.format(arch)
|
|
|
|
if get_qt_account_path().exists():
|
|
os.remove(get_qt_account_path())
|
|
|
|
cli = Cli()
|
|
cli._setup_settings()
|
|
cli.run(cmd.split())
|
|
|
|
out, err = capsys.readouterr()
|
|
assert expected_err in err or expected_err in out
|
|
|
|
|
|
def test_package_manager_init():
|
|
"""Test QtPackageManager initialization"""
|
|
manager = QtPackageManager(
|
|
arch="gcc_64",
|
|
version=Version("6.8.0"),
|
|
target="desktop",
|
|
username=TEST_EMAIL,
|
|
password=TEST_PASSWORD,
|
|
)
|
|
assert manager.arch == "gcc_64"
|
|
assert str(manager.version) == "6.8.0"
|
|
assert manager.target == "desktop"
|
|
assert manager.username == TEST_EMAIL
|
|
assert manager.password == TEST_PASSWORD
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"xml_content, expected_packages",
|
|
[
|
|
(
|
|
MOCK_XML_RESPONSE,
|
|
[
|
|
QtPackageInfo(name="qt.qt6.680.gcc_64", displayname="Desktop gcc", version="6.8.0-0-202312011"),
|
|
QtPackageInfo(name="qt.qt6.680.addons.qtquick3d", displayname="Qt Quick 3D", version="6.8.0-0-202312011"),
|
|
],
|
|
)
|
|
],
|
|
)
|
|
def test_parse_packages_xml(xml_content: str, expected_packages: List[QtPackageInfo]):
|
|
"""Test parsing of package XML data"""
|
|
manager = QtPackageManager(arch="gcc_64", version=Version("6.8.0"), target="desktop")
|
|
manager._parse_packages_xml(xml_content)
|
|
|
|
assert len(manager.packages) == len(expected_packages)
|
|
for actual, expected in zip(manager.packages, expected_packages):
|
|
assert actual.name == expected.name
|
|
assert actual.displayname == expected.displayname
|
|
assert actual.version == expected.version
|
|
|
|
|
|
def test_commercial_installer_auto_answers():
|
|
"""Test generation of auto-answer options"""
|
|
auto_answers = CommercialInstaller.get_auto_answers()
|
|
assert "OperationDoesNotExistError=Ignore" in auto_answers
|
|
assert "OverwriteTargetDirectory=No" in auto_answers
|
|
assert "telemetry-question=No" in auto_answers
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"installer_path, override, username, password, output_dir, no_unattended, expected_cmd",
|
|
[
|
|
(
|
|
"/path/to/installer",
|
|
None,
|
|
"user",
|
|
"pass",
|
|
"./output",
|
|
False,
|
|
[
|
|
"/path/to/installer",
|
|
"--accept-licenses",
|
|
"--accept-obligations",
|
|
"--confirm-command",
|
|
"--email",
|
|
"user",
|
|
"--pw",
|
|
"pass",
|
|
"--root",
|
|
str(Path("./output").absolute()),
|
|
"--auto-answer",
|
|
CommercialInstaller.get_auto_answers(),
|
|
],
|
|
),
|
|
(
|
|
"/path/to/installer",
|
|
["--override", "arg1", "arg2"],
|
|
None,
|
|
None,
|
|
None,
|
|
True,
|
|
["/path/to/installer", "--override", "arg1", "arg2"],
|
|
),
|
|
],
|
|
)
|
|
def test_build_command(
|
|
installer_path: str,
|
|
override: Optional[List[str]],
|
|
username: Optional[str],
|
|
password: Optional[str],
|
|
output_dir: Optional[str],
|
|
no_unattended: bool,
|
|
expected_cmd: List[str],
|
|
):
|
|
"""Test building of installer command"""
|
|
cmd = CommercialInstaller.build_command(
|
|
installer_path,
|
|
override=override,
|
|
username=username,
|
|
password=password,
|
|
output_dir=output_dir,
|
|
no_unattended=no_unattended,
|
|
)
|
|
assert cmd == expected_cmd
|
|
|
|
|
|
@pytest.mark.enable_socket
|
|
@pytest.mark.parametrize(
|
|
"os, osarch, expected_suffix",
|
|
[
|
|
pytest.param("windows", "windows", ".exe"),
|
|
pytest.param("windows", "windows-x64", ".exe"),
|
|
pytest.param("windows", "windows-arm64", ".exe"),
|
|
pytest.param("linux", "linux", ".run"),
|
|
pytest.param("linux", "linux-x64", ".run"),
|
|
pytest.param("linux", "linux-arm64", ".run"),
|
|
pytest.param("mac", "mac-x64", ".dmg"),
|
|
],
|
|
)
|
|
def test_commercial_installer_names(monkeypatch, os, osarch, expected_suffix):
|
|
"""Test installer names finder"""
|
|
|
|
monkeypatch.setattr("aqt.helper.get_os_arch", lambda: osarch)
|
|
|
|
installer_name = get_qt_installer_name()
|
|
|
|
assert installer_name.endswith(expected_suffix)
|
|
|
|
if os == get_os_name() and osarch in ["windows", "linux", "mac-x64"]:
|
|
target_path = Settings.qt_installer_temp_path / Path(installer_name)
|
|
base_url = Settings.baseurl
|
|
timeout = (Settings.connection_timeout, Settings.response_timeout)
|
|
download_installer(base_url, installer_name, target_path, timeout)
|
|
assert True
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"modules, expected_command",
|
|
[
|
|
(None, ["install", "qt.qt6.680.gcc_64"]),
|
|
(["qtquick3d"], ["install", "qt.qt6.680.gcc_64", "qt.qt6.680.addons.qtquick3d"]),
|
|
(["all"], ["install", "qt.qt6.680.gcc_64", "qt.qt6.680.addons.qtquick3d"]),
|
|
],
|
|
)
|
|
def test_get_install_command(monkeypatch, modules: Optional[List[str]], expected_command: List[str]):
|
|
"""Test generation of install commands"""
|
|
manager = QtPackageManager(arch="gcc_64", version=Version("6.8.0"), target="desktop")
|
|
|
|
def mock_gather_packages(self, installer_path: str) -> None:
|
|
self.packages = [
|
|
QtPackageInfo(name="qt.qt6.680.gcc_64", displayname="Desktop gcc", version="6.8.0"),
|
|
QtPackageInfo(name="qt.qt6.680.addons.qtquick3d", displayname="Qt Quick 3D", version="6.8.0"),
|
|
]
|
|
|
|
monkeypatch.setattr(QtPackageManager, "gather_packages", mock_gather_packages)
|
|
|
|
command = manager.get_install_command(modules, "./temp")
|
|
assert command == expected_command
|
|
|
|
|
|
@pytest.mark.enable_socket
|
|
@pytest.mark.parametrize(
|
|
"cmd, arch_dict, details, expected_command",
|
|
[
|
|
(
|
|
"install-qt-official desktop {} 6.8.1 " "--outputdir ./install-qt-official --email {} --pw {}",
|
|
{"windows": "win64_msvc2022_64", "linux": "linux_gcc_64", "mac": "clang_64"},
|
|
["./install-qt-official", "qt6", "681"],
|
|
"qt-unified-{}-x64-online.run --email ******** --pw ******** --root {} "
|
|
"--accept-licenses --accept-obligations "
|
|
"--confirm-command "
|
|
"--auto-answer OperationDoesNotExistError=Ignore,OverwriteTargetDirectory=No,"
|
|
"stopProcessesForUpdates=Cancel,installationErrorWithCancel=Cancel,installationErrorWithIgnore=Ignore,"
|
|
"AssociateCommonFiletypes=Yes,telemetry-question=No install qt.{}.{}.{}",
|
|
),
|
|
(
|
|
"install-qt-official desktop {} 6.8.1 --outputdir ./install-qt-official --email {} --pw {}",
|
|
{"windows": "win64_msvc2022_64", "linux": "linux_gcc_64", "mac": "clang_64"},
|
|
["./install-qt-official", "qt6", "681"],
|
|
"qt-unified-{}-x64-online.run --email ******** --pw ******** --root {} "
|
|
"--accept-licenses --accept-obligations "
|
|
"--confirm-command "
|
|
"--auto-answer OperationDoesNotExistError=Ignore,OverwriteTargetDirectory=Yes,"
|
|
"stopProcessesForUpdates=Cancel,installationErrorWithCancel=Cancel,installationErrorWithIgnore=Ignore,"
|
|
"AssociateCommonFiletypes=Yes,telemetry-question=No install qt.{}.{}.{}",
|
|
),
|
|
(
|
|
"install-qt linux desktop 6.8.1 {} --outputdir ./install-qt-flag --use-official-installer {} {}",
|
|
{"windows": "win64_msvc2022_64", "linux": "linux_gcc_64", "mac": "clang_64"},
|
|
["./install-qt-official", "qt6", "681"],
|
|
"{} --email ******** --pw ******** --root {} "
|
|
"--accept-licenses --accept-obligations "
|
|
"--confirm-command "
|
|
"--auto-answer OperationDoesNotExistError=Ignore,OverwriteTargetDirectory=Yes,"
|
|
"stopProcessesForUpdates=Cancel,installationErrorWithCancel=Cancel,installationErrorWithIgnore=Ignore,"
|
|
"AssociateCommonFiletypes=Yes,telemetry-question=No install qt.{}.{}.{}",
|
|
),
|
|
],
|
|
)
|
|
def test_install_qt_commercial(
|
|
capsys, tmp_path, monkeypatch, cmd: str, arch_dict: dict[str, str], details: list[str], expected_command: str
|
|
) -> None:
|
|
"""Test commercial Qt installation command"""
|
|
|
|
def mock_safely_run(*args, **kwargs):
|
|
return CompletedProcess(args=args[0], returncode=0)
|
|
|
|
def mock_get_default_local_cache_path(*args, **kwargs):
|
|
return tmp_path.joinpath("cache")
|
|
|
|
monkeypatch.setattr("aqt.commercial.safely_run", mock_safely_run)
|
|
monkeypatch.setattr("aqt.helper.get_default_local_cache_path", mock_get_default_local_cache_path)
|
|
|
|
current_platform = sys.platform.lower()
|
|
arch = arch_dict[current_platform]
|
|
|
|
abs_out = Path(details[0]).absolute()
|
|
|
|
# Get the email and password from the test parameters
|
|
email = TEST_EMAIL
|
|
password = TEST_PASSWORD
|
|
|
|
formatted_cmd = cmd.format(arch, email, password)
|
|
if expected_command.startswith("install-qt-official"):
|
|
formatted_expected = expected_command.format(current_platform, abs_out, *details[1:], arch)
|
|
else:
|
|
formatted_expected = expected_command.format(get_qt_installer_name(), abs_out, *details[1:], arch)
|
|
|
|
cli = Cli()
|
|
cli._setup_settings()
|
|
|
|
# First test the normal installation command
|
|
try:
|
|
cli.run(formatted_cmd.split())
|
|
except AttributeError:
|
|
out = " ".join(capsys.readouterr())
|
|
assert str(out).find(formatted_expected) >= 0
|
|
|
|
abs_out.joinpath(f"6.8.{str(details[2])[-1]}").mkdir(exist_ok=True, parents=True)
|
|
|
|
# Create a new command with the temp directory
|
|
new_cmd = (
|
|
f"install-qt-official desktop {arch} 6.8.{str(details[2])[-1]} --outputdir {abs_out} --email {email} "
|
|
f"--pw {password}"
|
|
)
|
|
|
|
# This should raise DiskAccessNotPermitted only for the first test (680)
|
|
if details[2] == "680":
|
|
with pytest.raises(DiskAccessNotPermitted) as exc_info:
|
|
cli.run(new_cmd.split())
|
|
assert "Target directory" in str(exc_info.value)
|
|
assert "already exists" in str(exc_info.value)
|
|
else:
|
|
cli.run(new_cmd.split())
|
|
|
|
def modify_qt_config(content, endwith):
|
|
"""
|
|
Takes content of INI file as string and returns modified content
|
|
"""
|
|
lines = content.splitlines()
|
|
in_qt_commercial = False
|
|
modified = []
|
|
|
|
for line in lines:
|
|
if line.strip() == "[qtofficial]":
|
|
in_qt_commercial = True
|
|
|
|
if endwith is not None and endwith in ["Yes", "No"] and "overwrite_target_directory : " in line:
|
|
line = f"overwrite_target_directory : {endwith}"
|
|
elif in_qt_commercial and "overwrite_target_directory : No" in line:
|
|
line = "overwrite_target_directory : Yes"
|
|
elif in_qt_commercial and "overwrite_target_directory : Yes" in line:
|
|
line = "overwrite_target_directory : No"
|
|
|
|
modified.append(line)
|
|
|
|
return "\n".join(modified)
|
|
|
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
config_path = os.path.join(script_dir, "../aqt/settings.ini")
|
|
|
|
with open(config_path, "r") as f:
|
|
content = f.read()
|
|
|
|
if expected_command.startswith("install-qt-official"):
|
|
modified_content = modify_qt_config(content, None)
|
|
else:
|
|
modified_content = modify_qt_config(content, "No")
|
|
|
|
with open(config_path, "w") as f:
|
|
f.write(modified_content)
|
|
Settings._initialize()
|
|
|
|
shutil.rmtree(abs_out)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"args, expected_error",
|
|
[
|
|
(["list-qt-official", "--bad-flag"], "usage: aqt [-h] [-c CONFIG]"),
|
|
],
|
|
)
|
|
def test_list_qt_commercial_errors(capsys, args, expected_error):
|
|
"""Test error handling in list-qt-official command"""
|
|
cli = Cli()
|
|
with pytest.raises(SystemExit):
|
|
cli.run(args)
|
|
_, err = capsys.readouterr()
|
|
assert expected_error in err
|