mirror of
https://github.com/miurahr/aqtinstall.git
synced 2025-12-17 12:44:38 +03:00
In an earlier PR, I added calls to `disable_socket()` from `pytest_socket` where I thought they were needed to prevent some tests from accessing the network, in case they weren't monkeypatched properly. Today, I discovered that `disable_socket()` disables sockets globally for all tests, which means that the tests that use remote data cannot run if they are executed after another test calls `disable_socket()`. This change calls `disable_socket()` once from `conftest.py`, so that no tests are allowed to use network data unless they are marked as ok to use the network, with `@pytest.mark.enable_socket`. See example of usage in `tests/test_connection.py`. Changed return code for unexpected exceptions: This allows us to write tests that can discover whether an unexpected exception occurred just by checking the return code, rather than reading stderr. This will allow us to write less friable tests that don't break every time some insignificant output details change. This change catches exceptions derived from Exception and KeyboardInterrupt raised by `installer`, while run by multiple processes, and propagates them back to earlier stack entries. This will prevent any OSError and BrokenPipe exceptions that would otherwise be raised when one process has an exception while the other processes are still running. This also handles the MemoryError exception we saw in #416, and offers some suggestions for solving the issue.
315 lines
11 KiB
Python
315 lines
11 KiB
Python
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from aqt.exceptions import CliInputError
|
|
from aqt.installer import Cli
|
|
from aqt.metadata import MetadataFactory, SimpleSpec, Version
|
|
|
|
|
|
@pytest.fixture()
|
|
def expected_help():
|
|
return (
|
|
"usage: aqt [-h] [-c CONFIG]\n"
|
|
" {install-qt,install-tool,install-doc,install-example,install-src,list-qt,list-tool,"
|
|
"install,tool,doc,examples,src,help,version}\n"
|
|
" ...\n"
|
|
"\n"
|
|
"Another unofficial Qt Installer.\n"
|
|
"aqt helps you install Qt SDK, tools, examples and others\n"
|
|
"\n"
|
|
"optional arguments:\n"
|
|
" -h, --help show this help message and exit\n"
|
|
" -c CONFIG, --config CONFIG\n"
|
|
" Configuration ini file.\n"
|
|
"\n"
|
|
"subcommands:\n"
|
|
" aqt accepts several subcommands:\n"
|
|
" install-* subcommands are commands that install components\n"
|
|
" list-* subcommands are commands that show available components\n"
|
|
" \n"
|
|
" commands {install|tool|src|examples|doc} are deprecated and marked for removal\n"
|
|
"\n"
|
|
" {install-qt,install-tool,install-doc,install-example,install-src,list-qt,list-tool,"
|
|
"install,tool,doc,examples,src,help,version}\n"
|
|
" Please refer to each help message by using '--help' with each subcommand\n"
|
|
)
|
|
|
|
|
|
def test_cli_help(capsys, expected_help):
|
|
cli = Cli()
|
|
cli.run(["help"])
|
|
out, err = capsys.readouterr()
|
|
assert out == expected_help
|
|
|
|
|
|
def test_cli_check_module():
|
|
cli = Cli()
|
|
cli._setup_settings()
|
|
assert cli._check_modules_arg("5.11.3", ["qtcharts", "qtwebengine"])
|
|
assert not cli._check_modules_arg("5.7", ["not_exist"])
|
|
assert cli._check_modules_arg("5.14.0", None)
|
|
assert not cli._check_modules_arg("5.15.0", ["Unknown"])
|
|
|
|
|
|
def test_cli_check_combination():
|
|
cli = Cli()
|
|
cli._setup_settings()
|
|
assert cli._check_qt_arg_combination("5.11.3", "linux", "desktop", "gcc_64")
|
|
assert cli._check_qt_arg_combination("5.11.3", "mac", "desktop", "clang_64")
|
|
assert not cli._check_qt_arg_combination("5.14.0", "android", "desktop", "clang_64")
|
|
|
|
|
|
def test_cli_check_version():
|
|
cli = Cli()
|
|
cli._setup_settings()
|
|
assert cli._check_qt_arg_versions("5.12.0")
|
|
assert not cli._check_qt_arg_versions("5.12")
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"host, target, arch, version_or_spec, expected_version, is_bad_spec",
|
|
(
|
|
("windows", "desktop", "wasm_32", "6.1", None, False),
|
|
("windows", "desktop", "wasm_32", "5.12", None, False),
|
|
("windows", "desktop", "wasm_32", "5.13", Version("5.13.2"), False),
|
|
("windows", "desktop", "wasm_32", "5", Version("5.15.2"), False),
|
|
("windows", "desktop", "wasm_32", "<5.14.5", Version("5.14.2"), False),
|
|
("windows", "desktop", "mingw32", "6.0", Version("6.0.3"), False),
|
|
("windows", "winrt", "mingw32", "6", None, False),
|
|
("windows", "winrt", "mingw32", "bad spec", None, True),
|
|
("windows", "android", "android_x86", "6", Version("6.1.0"), False),
|
|
("windows", "desktop", "android_x86", "6", Version("6.1.0"), False), # does not validate arch
|
|
("windows", "desktop", "android_fake", "6", Version("6.1.0"), False), # does not validate arch
|
|
),
|
|
)
|
|
def test_cli_determine_qt_version(
|
|
monkeypatch, host, target, arch, version_or_spec: str, expected_version: Version, is_bad_spec: bool
|
|
):
|
|
_html = (Path(__file__).parent / "data" / f"{host}-{target}.html").read_text("utf-8")
|
|
monkeypatch.setattr(MetadataFactory, "fetch_http", lambda self, _: _html)
|
|
cli = Cli()
|
|
cli._setup_settings()
|
|
|
|
if is_bad_spec:
|
|
with pytest.raises(CliInputError) as e:
|
|
Cli._determine_qt_version(version_or_spec, host, target, arch)
|
|
assert e.type == CliInputError
|
|
assert format(e.value) == f"Invalid version or SimpleSpec: '{version_or_spec}'\n" + SimpleSpec.usage()
|
|
elif not expected_version:
|
|
with pytest.raises(CliInputError) as e:
|
|
Cli._determine_qt_version(version_or_spec, host, target, arch)
|
|
assert e.type == CliInputError
|
|
expect_msg = f"No versions of Qt exist for spec={version_or_spec} with host={host}, target={target}, arch={arch}"
|
|
actual_msg = format(e.value)
|
|
assert actual_msg == expect_msg
|
|
else:
|
|
ver = Cli._determine_qt_version(version_or_spec, host, target, arch)
|
|
assert ver == expected_version
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"invalid_version",
|
|
("5.15", "five-dot-fifteen", "5", "5.5.5.5"),
|
|
)
|
|
def test_cli_invalid_version(capsys, invalid_version):
|
|
"""Checks that invalid version strings are handled properly"""
|
|
|
|
# Ensure that invalid_version cannot be a semantic_version.Version
|
|
with pytest.raises(ValueError):
|
|
Version(invalid_version)
|
|
|
|
cli = Cli()
|
|
cli._setup_settings()
|
|
|
|
matcher = re.compile(
|
|
r"^aqtinstall\(aqt\) v.* on Python 3.*\n"
|
|
r"(.*\n)*"
|
|
r".*Invalid version: '" + invalid_version + r"'! Please use the form '5\.X\.Y'\.\n.*"
|
|
)
|
|
|
|
for cmd in (
|
|
("install", invalid_version, "mac", "desktop"),
|
|
("doc", invalid_version, "mac", "desktop"),
|
|
("list-qt", "mac", "desktop", "--arch", invalid_version),
|
|
):
|
|
cli = Cli()
|
|
assert cli.run(cmd) == 1
|
|
out, err = capsys.readouterr()
|
|
sys.stdout.write(out)
|
|
sys.stderr.write(err)
|
|
assert matcher.match(err)
|
|
|
|
|
|
def test_cli_check_mirror():
|
|
cli = Cli()
|
|
cli._setup_settings()
|
|
assert cli._check_mirror(None)
|
|
arg = ["install-qt", "linux", "desktop", "5.11.3", "-b", "https://download.qt.io/"]
|
|
args = cli.parser.parse_args(arg)
|
|
assert args.base == "https://download.qt.io/"
|
|
assert cli._check_mirror(args.base)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"arch, host, target, version, expect",
|
|
(
|
|
(None, "windows", "desktop", "6.2.0", None),
|
|
("", "windows", "desktop", "6.2.0", None),
|
|
(None, "linux", "desktop", "6.2.0", "gcc_64"),
|
|
(None, "mac", "desktop", "6.2.0", "clang_64"),
|
|
(None, "mac", "ios", "6.2.0", "ios"),
|
|
(None, "mac", "android", "6.2.0", "android"),
|
|
(None, "mac", "android", "5.12.0", None),
|
|
),
|
|
)
|
|
def test_set_arch(arch, host, target, version, expect):
|
|
cli = Cli()
|
|
cli._setup_settings()
|
|
|
|
if not expect:
|
|
with pytest.raises(CliInputError) as e:
|
|
cli._set_arch([], arch, host, target, version)
|
|
assert e.type == CliInputError
|
|
assert format(e.value) == "Please supply a target architecture."
|
|
assert e.value.should_show_help is True
|
|
else:
|
|
assert cli._set_arch([], arch, host, target, version) == expect
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"cmd, expect_msg, should_show_help",
|
|
(
|
|
(
|
|
"install-qt mac ios 6.2.0 --base not_a_url",
|
|
"The `--base` option requires a url where the path `online/qtsdkrepository` exists.",
|
|
True,
|
|
),
|
|
(
|
|
"install-qt mac ios 6.2.0 --noarchives",
|
|
"When `--noarchives` is set, the `--modules` option is mandatory.",
|
|
False,
|
|
),
|
|
(
|
|
"install-qt mac ios 6.2.0 --noarchives --archives",
|
|
"When `--noarchives` is set, the `--modules` option is mandatory.",
|
|
False,
|
|
),
|
|
(
|
|
"install-qt mac ios 6.2.0 --noarchives --archives --modules qtcharts",
|
|
"Options `--archives` and `--noarchives` are mutually exclusive.",
|
|
False,
|
|
),
|
|
(
|
|
"install-src mac ios 6.2.0 --kde",
|
|
"KDE patch: unsupported version!!",
|
|
False,
|
|
),
|
|
),
|
|
)
|
|
def test_cli_input_errors(capsys, expected_help, cmd, expect_msg, should_show_help):
|
|
cli = Cli()
|
|
cli._setup_settings()
|
|
assert 1 == cli.run(cmd.split())
|
|
out, err = capsys.readouterr()
|
|
assert out == (expected_help if should_show_help else "")
|
|
assert err.rstrip().endswith(expect_msg)
|
|
|
|
|
|
# These commands use the new syntax with the legacy commands
|
|
@pytest.mark.parametrize(
|
|
"cmd",
|
|
(
|
|
"install linux desktop 5.10.0",
|
|
"install linux desktop 5.10.0 gcc_64",
|
|
"src linux desktop 5.10.0",
|
|
"doc linux desktop 5.10.0",
|
|
"example linux desktop 5.10.0",
|
|
"tool windows desktop tools_ifw",
|
|
),
|
|
)
|
|
def test_cli_legacy_commands_with_wrong_syntax(cmd):
|
|
cli = Cli()
|
|
cli._setup_settings()
|
|
with pytest.raises(SystemExit) as e:
|
|
cli.run(cmd.split())
|
|
assert e.type == SystemExit
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"cmd",
|
|
(
|
|
"tool windows desktop tools_ifw qt.tools.ifw.31", # New syntax
|
|
"tool windows desktop tools_ifw 1.2.3",
|
|
),
|
|
)
|
|
def test_cli_legacy_tool_new_syntax(monkeypatch, capsys, cmd):
|
|
# These incorrect commands cannot be filtered out directly by argparse because
|
|
# they have the correct number of arguments.
|
|
command = cmd.split()
|
|
|
|
expected = (
|
|
"Warning: The command 'tool' is deprecated and marked for removal in a future version of aqt.\n"
|
|
"In the future, please use the command 'install-tool' instead.\n"
|
|
"Invalid version: 'tools_ifw'! Please use the form '5.X.Y'.\n"
|
|
)
|
|
|
|
cli = Cli()
|
|
cli._setup_settings()
|
|
assert 1 == cli.run(command)
|
|
out, err = capsys.readouterr()
|
|
actual = err[err.index("\n") + 1 :]
|
|
assert actual == expected
|
|
|
|
|
|
# These commands come directly from examples in the legacy documentation
|
|
@pytest.mark.parametrize(
|
|
"cmd",
|
|
(
|
|
"install 5.10.0 linux desktop", # default arch
|
|
"install 5.10.2 linux android android_armv7",
|
|
"src 5.15.2 windows desktop --archives qtbase --kde",
|
|
"doc 5.15.2 windows desktop -m qtcharts qtnetworkauth",
|
|
"examples 5.15.2 windows desktop -m qtcharts qtnetworkauth",
|
|
"tool linux tools_ifw 4.0 qt.tools.ifw.40",
|
|
),
|
|
)
|
|
def test_cli_legacy_commands_with_correct_syntax(monkeypatch, cmd):
|
|
# Pretend to install correctly when any command is run
|
|
for func in ("run_install_qt", "run_install_src", "run_install_doc", "run_install_example", "run_install_tool"):
|
|
monkeypatch.setattr(Cli, func, lambda *args, **kwargs: 0)
|
|
|
|
cli = Cli()
|
|
cli._setup_settings()
|
|
assert 0 == cli.run(cmd.split())
|
|
|
|
|
|
def test_cli_unexpected_error(monkeypatch, capsys):
|
|
def _mocked_run(*args):
|
|
raise RuntimeError("Some unexpected error")
|
|
|
|
monkeypatch.setattr("aqt.installer.Cli.run_install_qt", _mocked_run)
|
|
|
|
cli = Cli()
|
|
cli._setup_settings()
|
|
assert Cli.UNHANDLED_EXCEPTION_CODE == cli.run(["install-qt", "mac", "ios", "6.2.0"])
|
|
out, err = capsys.readouterr()
|
|
assert err.startswith("Some unexpected error")
|
|
assert err.rstrip().endswith(
|
|
"===========================PLEASE FILE A BUG REPORT===========================\n"
|
|
"You have discovered a bug in aqt.\n"
|
|
"Please file a bug report at https://github.com/miurahr/aqtinstall/issues.\n"
|
|
"Please remember to include a copy of this program's output in your report."
|
|
)
|
|
|
|
|
|
def test_cli_set_7zip(monkeypatch):
|
|
cli = Cli()
|
|
cli._setup_settings()
|
|
with pytest.raises(CliInputError) as err:
|
|
cli._set_sevenzip("some_nonexistent_binary")
|
|
assert err.type == CliInputError
|
|
assert format(err.value) == "Specified 7zip command executable does not exist: 'some_nonexistent_binary'"
|