Files
aqtinstall/tests/test_list.py
Alexandre Poumaroux 5d699b9ebf Commercial fixes, CI tests, more tests, coverage (#883)
* Add authentication flags for list-qt-commercial, add tests for coverage

* Add dry run

* Make tests really use auth since secrets have been added, fix some indents

* Fix auth issue, rename user 'email, and password 'pw'

* Fix modules param type

* Update commands names
2025-02-22 09:56:56 +09:00

1339 lines
54 KiB
Python

import hashlib
import json
import os
import re
import shutil
import sys
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Dict, List, Optional, Set, Union
from urllib.parse import urlparse
import pytest
from aqt.exceptions import (
AqtException,
ArchiveConnectionError,
ArchiveDownloadError,
ArchiveListError,
CliInputError,
EmptyMetadata,
)
from aqt.helper import Settings
from aqt.installer import Cli
from aqt.metadata import (
ArchiveId,
MetadataFactory,
QtRepoProperty,
SimpleSpec,
ToolData,
Version,
Versions,
show_list,
suggested_follow_up,
)
ModulesQuery = MetadataFactory.ModulesQuery
Settings.load_settings()
@pytest.mark.parametrize(
"arch, version, expect",
(
("wasm_32", Version("5.13.1"), "wasm"),
("mingw", Version("5.13.1"), ""),
("android_fake", Version("5.13.1"), ""),
("android_x86", Version("5.13.1"), ""),
("android_x86", Version("6.13.1"), "x86"),
("android_x86", Version("6.0.0"), "x86"),
),
)
def test_list_extension_for_arch(arch: str, version: Version, expect: str):
ext = QtRepoProperty.extension_for_arch(arch, version >= Version("6.0.0"))
assert ext == expect
@pytest.mark.parametrize(
"arch, expect",
(
("wasm_32", ["wasm"]),
("mingw", [""]),
("android_fake", [""]),
("android", [""]),
("android_x86", ["", "x86"]),
),
)
def test_list_possible_extension_for_arch(arch: str, expect: List[str]):
exts = QtRepoProperty.possible_extensions_for_arch(arch)
assert set(exts) == set(expect)
@pytest.mark.parametrize(
"init_data, expect_str, expect_fmt, expect_flat, expect_last, expect_bool",
(
(
[
(1, [Version("1.1.1"), Version("1.1.2")]),
(2, [Version("1.2.1"), Version("1.2.2")]),
],
"[[Version('1.1.1'), Version('1.1.2')], [Version('1.2.1'), Version('1.2.2')]]",
"1.1.1 1.1.2\n1.2.1 1.2.2",
[Version("1.1.1"), Version("1.1.2"), Version("1.2.1"), Version("1.2.2")],
Version("1.2.2"),
True,
),
(
[],
"[]",
"",
[],
None,
False,
),
(
Version("1.2.3"),
"[[Version('1.2.3')]]",
"1.2.3",
[Version("1.2.3")],
Version("1.2.3"),
True,
),
),
)
def test_versions(init_data, expect_str, expect_fmt, expect_flat, expect_last, expect_bool):
versions = Versions(init_data)
assert str(versions) == expect_str
assert format(versions) == expect_fmt
assert format(versions, "s") == expect_str
assert versions.flattened() == expect_flat
assert versions.latest() == expect_last
assert bool(versions) == expect_bool
with pytest.raises(TypeError) as pytest_wrapped_e:
format(versions, "x")
assert pytest_wrapped_e.type == TypeError
@pytest.fixture
def spec_regex():
return re.compile(r"^(\d+\.\d+)")
@pytest.mark.parametrize(
"os_name,target,in_file,expect_out_file",
[
("windows", "android", "windows-android.html", "windows-android-expect.json"),
("windows", "desktop", "windows-desktop.html", "windows-desktop-expect.json"),
("windows", "winrt", "windows-winrt.html", "windows-winrt-expect.json"),
("linux", "android", "linux-android.html", "linux-android-expect.json"),
("linux", "desktop", "linux-desktop.html", "linux-desktop-expect.json"),
("linux_arm64", "desktop", "linux_arm64-desktop.html", "linux_arm64-desktop-expect.json"),
("mac", "android", "mac-android.html", "mac-android-expect.json"),
("mac", "desktop", "mac-desktop.html", "mac-desktop-expect.json"),
("mac", "ios", "mac-ios.html", "mac-ios-expect.json"),
("windows", "android", "mirror-pre-a.html", "mirror-expect.json"),
("windows", "android", "mirror-table-before-pre-a.html", "mirror-expect.json"),
("windows", "android", "mirror-first-td.html", "mirror-expect.json"),
("windows", "android", "mirror-tag-in-a.html", "mirror-expect.json"),
],
)
def test_list_versions_tools(monkeypatch, spec_regex, os_name, target, in_file, expect_out_file):
_html = (Path(__file__).parent / "data" / in_file).read_text("utf-8")
monkeypatch.setattr(MetadataFactory, "fetch_http", lambda *args, **kwargs: _html)
expected = json.loads((Path(__file__).parent / "data" / expect_out_file).read_text("utf-8"))
# Test 'aqt list-tool'
tools = MetadataFactory(ArchiveId("tools", os_name, target)).getList()
assert tools == expected["tools"]
# Test 'aqt list-qt'
expected_output = expected["qt"]["qt"]
archive_id = ArchiveId("qt", os_name, target)
all_versions = MetadataFactory(archive_id).getList()
assert f"{all_versions}" == "\n".join(expected_output)
# Filter for the latest version only
latest_ver = MetadataFactory(archive_id, is_latest_version=True).getList()
assert f"{latest_ver}" == expected_output[-1].split(" ")[-1]
for row in expected_output:
spec = SimpleSpec(spec_regex.search(row).group(1))
# Find the latest version for a particular spec
latest_ver_for_spec = MetadataFactory(
archive_id,
spec=spec,
is_latest_version=True,
).getList()
assert f"{latest_ver_for_spec}" == row.split(" ")[-1]
# Find all versions for a particular spec
all_ver_for_spec = MetadataFactory(
archive_id,
spec=spec,
).getList()
assert f"{all_ver_for_spec}" == row
@pytest.mark.parametrize(
"version,extension,in_file,expect_out_file",
[
("5.14.0", "", "windows-5140-update.xml", "windows-5140-expect.json"),
("5.15.0", "", "windows-5150-update.xml", "windows-5150-expect.json"),
(
"5.15.2",
"src_doc_examples",
"windows-5152-src-doc-example-update.xml",
"windows-5152-src-doc-example-expect.json",
),
("6.2.0", "", "windows-620-update.xml", "windows-620-expect.json"),
],
)
def test_list_qt_modules(monkeypatch, version: str, extension: str, in_file: str, expect_out_file: str):
archive_id = ArchiveId("qt", "windows", "desktop")
_xml = (Path(__file__).parent / "data" / in_file).read_text("utf-8")
expect = json.loads((Path(__file__).parent / "data" / expect_out_file).read_text("utf-8"))
monkeypatch.setattr(MetadataFactory, "fetch_http", lambda self, _: _xml)
for arch in expect["architectures"]:
modules = MetadataFactory(archive_id).fetch_modules(Version(version), arch)
assert modules == sorted(expect["modules_by_arch"][arch])
@pytest.fixture
def win_5152_sde_xml_file() -> str:
return (Path(__file__).parent / "data" / "windows-5152-src-doc-example-update.xml").read_text("utf-8")
def win_sde_expected(cmd_type: str, query_type: str, expect_out_file: str) -> Set[str]:
assert cmd_type in ("src", "doc", "examples")
assert query_type in ("archives", "modules")
_json = json.loads((Path(__file__).parent / "data" / expect_out_file).read_text("utf-8"))
return set(_json[cmd_type][query_type])
def win_5152_sde_expected(cmd_type: str, query_type: str) -> Set[str]:
return win_sde_expected(cmd_type, query_type, "windows-5152-src-doc-example-expect.json")
@pytest.mark.parametrize(
"cmd_type, host, version, expected",
[
(
_cmd_type,
"windows",
"5.15.2",
win_5152_sde_expected(_cmd_type, "archives"),
)
for _cmd_type in ("src", "doc", "examples")
],
)
def test_list_src_doc_examples_archives(
monkeypatch, win_5152_sde_xml_file, cmd_type: str, host: str, version: str, expected: Set[str]
):
monkeypatch.setattr(MetadataFactory, "fetch_http", lambda self, _: win_5152_sde_xml_file)
archive_id = ArchiveId("qt", host, "desktop")
archives = set(MetadataFactory(archive_id).fetch_archives_sde(cmd_type, Version(version)))
assert archives == expected
@pytest.mark.parametrize(
"cmd_type, host, version, expected",
[
(
_cmd_type,
"windows",
"5.15.2",
win_5152_sde_expected(_cmd_type, "modules"),
)
for _cmd_type in ("doc", "examples")
],
)
def test_list_src_doc_examples_modules(
monkeypatch, win_5152_sde_xml_file, cmd_type: str, host: str, version: str, expected: Set[str]
):
monkeypatch.setattr(MetadataFactory, "fetch_http", lambda self, _: win_5152_sde_xml_file)
archive_id = ArchiveId("qt", host, "desktop")
modules = set(MetadataFactory(archive_id).fetch_modules_sde(cmd_type, Version(version)))
assert modules == expected
@pytest.mark.parametrize(
"command, updates_file, url, expected",
(
(
"list-src windows 5.15.2",
"windows-5152-src-doc-example-update.xml",
re.compile(r"/windows_x86/desktop/qt5_5152_src_doc_examples/Updates\.xml$"),
win_5152_sde_expected("src", "archives"),
),
(
"list-doc windows 5.15.2",
"windows-5152-src-doc-example-update.xml",
re.compile(r"/windows_x86/desktop/qt5_5152_src_doc_examples/Updates\.xml$"),
win_5152_sde_expected("doc", "archives"),
),
(
"list-example windows 5.15.2",
"windows-5152-src-doc-example-update.xml",
re.compile(r"/windows_x86/desktop/qt5_5152_src_doc_examples/Updates\.xml$"),
win_5152_sde_expected("examples", "archives"),
),
(
"list-doc windows 5.15.2 --modules",
"windows-5152-src-doc-example-update.xml",
re.compile(r"/windows_x86/desktop/qt5_5152_src_doc_examples/Updates\.xml$"),
win_5152_sde_expected("doc", "modules"),
),
(
"list-example windows 5.15.2 --modules",
"windows-5152-src-doc-example-update.xml",
re.compile(r"/windows_x86/desktop/qt5_5152_src_doc_examples/Updates\.xml$"),
win_5152_sde_expected("examples", "modules"),
),
(
"list-src windows 6.8.1",
"all_os-681-src-doc-example-update.xml",
re.compile(r"/all_os/qt/qt6_681_(?:unix|windows)_line_endings_src/Updates\.xml$"),
win_sde_expected("src", "archives", "all_os-681-src-doc-example-expect.json"),
),
(
"list-doc windows 6.8.1",
"all_os-681-src-doc-example-update.xml",
re.compile(r"/all_os/qt/qt6_681_(?:unix|windows)_line_endings_src/Updates\.xml$"),
win_sde_expected("doc", "archives", "all_os-681-src-doc-example-expect.json"),
),
(
"list-example windows 6.8.1",
"all_os-681-src-doc-example-update.xml",
re.compile(r"/all_os/qt/qt6_681_(?:unix|windows)_line_endings_src/Updates\.xml$"),
win_sde_expected("examples", "archives", "all_os-681-src-doc-example-expect.json"),
),
(
"list-src all_os 6.8.1",
"all_os-681-src-doc-example-update.xml",
re.compile(r"/all_os/qt/qt6_681_(?:unix|windows)_line_endings_src/Updates\.xml$"),
win_sde_expected("src", "archives", "all_os-681-src-doc-example-expect.json"),
),
(
"list-doc all_os 6.8.1",
"all_os-681-src-doc-example-update.xml",
re.compile(r"/all_os/qt/qt6_681_(?:unix|windows)_line_endings_src/Updates\.xml$"),
win_sde_expected("doc", "archives", "all_os-681-src-doc-example-expect.json"),
),
(
"list-example all_os 6.8.1",
"all_os-681-src-doc-example-update.xml",
re.compile(r"/all_os/qt/qt6_681_(?:unix|windows)_line_endings_src/Updates\.xml$"),
win_sde_expected("examples", "archives", "all_os-681-src-doc-example-expect.json"),
),
),
)
def test_list_src_doc_examples_cli(
monkeypatch, capsys, command: str, updates_file: str, url: re.Pattern, expected: Set[str]
):
def mock_fetch(self, rest_of_url):
assert url.search(rest_of_url), f"Unexpected URL: {rest_of_url}"
return (Path(__file__).parent / "data" / updates_file).read_text("utf-8")
monkeypatch.setattr(MetadataFactory, "fetch_http", mock_fetch)
cli = Cli()
assert 0 == cli.run(command.split())
out, err = capsys.readouterr()
assert not err
out_set = set(out.strip().split())
assert out_set == expected
@pytest.mark.parametrize(
"version, arch, modules_to_query, modules_failed_query",
(
("5.14.0", "win32_mingw73", [], []),
("5.14.0", "win32_mingw73", ["qtcharts"], []),
("5.14.0", "win32_mingw73", ["all"], []),
("5.14.0", "win32_mingw73", ["debug_info"], ["debug_info"]),
("5.14.0", "win64_msvc2017_64", [], []),
("5.14.0", "win64_msvc2017_64", ["debug_info"], []),
),
)
def test_list_archives(
monkeypatch, capsys, version: str, arch: str, modules_to_query: List[str], modules_failed_query: List[str]
):
archive_id = ArchiveId("qt", "windows", "desktop")
in_file = f"{archive_id.host}-{version.replace('.', '')}-update.xml"
expect_out_file = f"{archive_id.host}-{version.replace('.', '')}-expect.json"
_xml = (Path(__file__).parent / "data" / in_file).read_text("utf-8")
monkeypatch.setattr(MetadataFactory, "fetch_http", lambda self, _: _xml)
expect = json.loads((Path(__file__).parent / "data" / expect_out_file).read_text("utf-8"))
if not modules_to_query:
expected_qt_archives = expect["qt_base_pkgs_by_arch"][arch]["DownloadableArchives"]
expected = set([arc.split("-")[0] for arc in expected_qt_archives])
else:
expected_mod_metadata = expect["modules_metadata_by_arch"][arch]
if "all" not in modules_to_query:
expected_mod_metadata = [mod for mod in expected_mod_metadata if mod["Name"].split(".")[-2] in modules_to_query]
expected = set([arc.split("-")[0] for mod in expected_mod_metadata for arc in mod["DownloadableArchives"]])
archives_query = [version, arch, *modules_to_query]
cli_args = ["list-qt", "windows", "desktop", "--archives", *archives_query]
if not modules_failed_query:
meta = set(MetadataFactory(archive_id, archives_query=archives_query).getList())
assert meta == expected
cli = Cli()
assert 0 == cli.run(cli_args)
out, err = capsys.readouterr()
assert out.rstrip() == " ".join(sorted(expected))
return
expected_err_msg = f"The requested modules were not located: {sorted(modules_failed_query)}"
with pytest.raises(CliInputError) as err:
MetadataFactory(archive_id, archives_query=archives_query).getList()
assert err.type == CliInputError
assert format(err.value).startswith(expected_err_msg)
cli = Cli()
assert 1 == cli.run(cli_args)
out, err = capsys.readouterr()
assert expected_err_msg in err
def test_list_archives_insufficient_args(capsys):
cli = Cli()
assert 1 == cli.run("list-qt mac desktop --archives 5.14.0".split())
out, err = capsys.readouterr()
assert err.strip() == "ERROR : The '--archives' flag requires a 'QT_VERSION' and an 'ARCHITECTURE' parameter."
@pytest.mark.parametrize(
"xml_content",
(
"<Updates><PackageUpdate><badname></badname></PackageUpdate></Updates>",
"<Updates><PackageUpdate><Name></Name></PackageUpdate></Updates>",
"<Updates></PackageUpdate><PackageUpdate></Updates><Name></Name>",
),
)
def test_list_archives_bad_xml(monkeypatch, xml_content: str):
archive_id = ArchiveId("qt", "windows", "desktop")
archives_query = ["5.15.2", "win32_mingw81", "qtcharts"]
monkeypatch.setattr(MetadataFactory, "fetch_http", lambda self, _: xml_content)
with pytest.raises(ArchiveListError) as e:
MetadataFactory(archive_id, archives_query=archives_query).getList()
assert e.type == ArchiveListError
@pytest.mark.parametrize(
"host, target, tool_name",
[
("mac", "desktop", "tools_cmake"),
("mac", "desktop", "tools_ifw"),
("mac", "desktop", "tools_qtcreator"),
],
)
def test_tool_modules(monkeypatch, host: str, target: str, tool_name: str):
archive_id = ArchiveId("tools", host, target)
in_file = "{}-{}-{}-update.xml".format(host, target, tool_name)
expect_out_file = "{}-{}-{}-expect.json".format(host, target, tool_name)
_xml = (Path(__file__).parent / "data" / in_file).read_text("utf-8")
expect = json.loads((Path(__file__).parent / "data" / expect_out_file).read_text("utf-8"))
monkeypatch.setattr(MetadataFactory, "fetch_http", lambda self, _: _xml)
modules = MetadataFactory(archive_id, tool_name=tool_name).getList()
assert modules == expect["modules"]
table = MetadataFactory(archive_id, tool_name=tool_name, is_long_listing=True).getList()
assert table._rows(table.long_heading_keys) == expect["long_listing"]
@pytest.mark.parametrize(
"host, target, version, arch",
[
["windows", "desktop", "5.14.0", arch]
for arch in ["win32_mingw73", "win32_msvc2017", "win64_mingw73", "win64_msvc2015_64", "win64_msvc2017_64"]
],
)
def test_long_qt_modules(monkeypatch, host: str, target: str, version: str, arch: str):
archive_id = ArchiveId("qt", host, target)
in_file = f"{host}-{version.replace('.', '')}-update.xml"
expect_out_file = f"{host}-{version.replace('.', '')}-expect.json"
_xml = (Path(__file__).parent / "data" / in_file).read_text("utf-8")
expect = json.loads((Path(__file__).parent / "data" / expect_out_file).read_text("utf-8"))
monkeypatch.setattr(MetadataFactory, "fetch_http", lambda self, _: _xml)
table = MetadataFactory(archive_id, modules_query=ModulesQuery(version, arch), is_long_listing=True).getList()
assert table._rows(table.long_heading_keys) == expect["modules_long_by_arch"][arch]
def expected_windows_desktop_plus_wasm_5140(is_wasm_threaded: bool) -> Dict:
if is_wasm_threaded:
input_filenames = (
"windows-5140-expect.json",
"windows-650-wasm-single-expect.json",
"windows-650-wasm-multi-expect.json",
)
else:
input_filenames = "windows-5140-expect.json", "windows-5140-wasm-expect.json"
to_join = [json.loads((Path(__file__).parent / "data" / f).read_text("utf-8")) for f in input_filenames]
result = {"architectures": [], "modules_by_arch": {}}
# Gather architectures from all sources
for source in to_join:
result["architectures"].extend(source["architectures"])
if "modules_by_arch" in source:
result["modules_by_arch"].update(source["modules_by_arch"])
# Remove duplicates while preserving order
seen = set()
result["architectures"] = [x for x in result["architectures"] if not (x in seen or seen.add(x))]
return result
@pytest.mark.parametrize(
"host, target, version, arch, expect_arches",
[
("all_os", "wasm", "6.7.3", "", {"wasm_singlethread", "wasm_multithread"}),
("all_os", "wasm", "6.8.0", "", {"wasm_singlethread", "wasm_multithread"}),
],
)
def test_list_wasm_arches(monkeypatch, capsys, host: str, target: str, version: str, arch: str, expect_arches: Set[str]):
def _mock_fetch_http(_, rest_of_url: str, *args, **kwargs) -> str:
if rest_of_url.endswith("wasm_singlethread/Updates.xml"):
if version >= "6.8.0":
return (Path(__file__).parent / "data" / "all_os-680-wasm-single-update.xml").read_text("utf-8")
else:
return (Path(__file__).parent / "data" / "all_os-673-wasm-single-update.xml").read_text("utf-8")
elif rest_of_url.endswith("wasm_multithread/Updates.xml"):
if version >= "6.8.0":
return (Path(__file__).parent / "data" / "all_os-680-wasm-multi-update.xml").read_text("utf-8")
else:
return (Path(__file__).parent / "data" / "all_os-673-wasm-multi-update.xml").read_text("utf-8")
return "" # Return empty HTML since we don't need it
monkeypatch.setattr("aqt.metadata.getUrl", _mock_fetch_http)
monkeypatch.setattr(MetadataFactory, "fetch_http", _mock_fetch_http)
cli = Cli()
cli._setup_settings()
assert 0 == cli.run(["list-qt", host, target, "--arch", version])
out, err = capsys.readouterr()
assert set(out.strip().split()) == expect_arches
@pytest.mark.parametrize(
"args, is_wasm_threaded, expect",
(
("--modules latest win64_msvc2017_64", False, ["modules_by_arch", "win64_msvc2017_64"]),
("--spec 5.14 --modules latest win64_msvc2017_64", False, ["modules_by_arch", "win64_msvc2017_64"]),
("--modules 5.14.0 win32_mingw73", False, ["modules_by_arch", "win32_mingw73"]),
("--modules 5.14.0 win32_msvc2017", False, ["modules_by_arch", "win32_msvc2017"]),
("--modules 5.14.0 win64_mingw73", False, ["modules_by_arch", "win64_mingw73"]),
("--modules 5.14.0 win64_msvc2015_64", False, ["modules_by_arch", "win64_msvc2015_64"]),
("--modules 5.14.0 win64_msvc2017_64", False, ["modules_by_arch", "win64_msvc2017_64"]),
("--modules 6.5.0 wasm_singlethread", True, ["modules_by_arch", "wasm_singlethread"]),
("--modules 6.5.0 wasm_multithread", True, ["modules_by_arch", "wasm_multithread"]),
("--spec 5.14 --arch latest", False, ["architectures"]),
("--arch 5.14.0", False, ["architectures"]),
),
)
def test_list_qt_cli(
monkeypatch,
capsys,
args: str,
is_wasm_threaded: bool,
expect: Union[Set[str], List[str]],
):
version_string_to_replace = "qt5.5140"
if isinstance(expect, list):
# In this case, 'expect' is a list of keys to follow to the expected values.
expected_dict = expected_windows_desktop_plus_wasm_5140(is_wasm_threaded)
for key in expect: # Follow the chain of keys to the list of values.
expected_dict = expected_dict[key]
assert isinstance(expected_dict, list)
expect_set = set(expected_dict)
else:
expect_set = expect
assert isinstance(expect_set, set)
def _mock_fetch_http(_, rest_of_url, *args, **kwargs: str) -> str:
htmltext = (Path(__file__).parent / "data" / "windows-desktop.html").read_text("utf-8")
if not rest_of_url.endswith("Updates.xml"):
return htmltext
def get_xml_filename() -> str:
if rest_of_url.endswith("_wasm/Updates.xml"):
return "windows-5140-wasm-update.xml"
elif rest_of_url.endswith("_wasm_singlethread/Updates.xml"):
return "windows-650-wasm-single-update.xml"
elif rest_of_url.endswith("_wasm_multithread/Updates.xml"):
return "windows-650-wasm-multi-update.xml"
else:
return "windows-5140-update.xml"
xmltext = (Path(__file__).parent / "data" / get_xml_filename()).read_text("utf-8")
# If we are serving an Updates.xml, `aqt list` will look for a Qt version number.
# We will replace the version numbers in the file with the requested version.
match = re.search(r"qt(\d)_(\d+)", rest_of_url)
assert match
major, version_nodot = match.groups()
desired_version_string = f"qt{major}.{version_nodot}"
return xmltext.replace(version_string_to_replace, desired_version_string)
monkeypatch.setattr(MetadataFactory, "fetch_http", _mock_fetch_http)
cli = Cli()
cli.run(["list-qt", "windows", "desktop", *args.split()])
out, err = capsys.readouterr()
output_set = set(out.strip().split())
assert output_set == expect_set
def test_list_missing_wasm_updates_for_windows(monkeypatch, capsys):
"""Require that MetadataFactory is resilient to missing wasm updates.xml files"""
data_dir = Path(__file__).parent / "data"
expect = set(json.loads((data_dir / "windows-620-expect.json").read_text("utf-8"))["architectures"])
def _mock_fetch_http(_, rest_of_url, *args, **kwargs: str) -> str:
htmltext = (Path(__file__).parent / "data" / "windows-desktop.html").read_text("utf-8")
if rest_of_url.endswith("windows_x86/desktop/"):
return htmltext
elif rest_of_url.endswith("windows_x86/desktop/qt6_620/Updates.xml"):
return (data_dir / "windows-620-update.xml").read_text("utf-8")
else:
raise ArchiveDownloadError(f"No such file at {rest_of_url}")
monkeypatch.setattr(MetadataFactory, "fetch_http", _mock_fetch_http)
cli = Cli()
rv = cli.run("list-qt windows desktop --arch 6.2.0".split())
assert rv == 0
out, err = capsys.readouterr()
assert set(out.strip().split()) == expect
@pytest.mark.parametrize(
"qt_ver_str, expect_set", (("6.2.0", {"android_x86", "android_x86_64", "android_armv7", "android_arm64_v8a"}),)
)
def test_list_android_arches(monkeypatch, capsys, qt_ver_str: str, expect_set: Set[str]):
host, target = "windows", "android"
def _mock_fetch_http(_, rest_of_url, *args, **kwargs: str) -> str:
assert rest_of_url.endswith("Updates.xml"), f"Fetched unexpected file at {rest_of_url}"
xmltext = (Path(__file__).parent / "data" / "windows-620-android-armv7-update.xml").read_text("utf-8")
# If we are serving an Updates.xml, `aqt list` will look for a Qt version number.
# We will replace the version numbers in the file with the requested version.
match = re.search(r"qt\d_\d+_(?P<arch>\w+)/Updates\.xml$", rest_of_url)
assert match
return xmltext.replace("armv7", match.group("arch"))
monkeypatch.setattr(MetadataFactory, "fetch_http", _mock_fetch_http)
# Unit test:
archive_id = ArchiveId("qt", host, target)
actual_arches = MetadataFactory(archive_id).fetch_arches(Version(qt_ver_str))
assert set(actual_arches) == expect_set
# Integration test:
cli = Cli()
cli.run(["list-qt", host, target, "--arch", qt_ver_str])
out, err = capsys.readouterr()
output_set = set(out.strip().split())
assert output_set == expect_set
@pytest.mark.parametrize(
"host, has_wasm",
(("linux", True), ("windows", False), ("mac", False)),
)
def test_list_desktop_arches_qt5130(monkeypatch, capsys, host: str, has_wasm: bool):
"""Tests the edge case of desktop Qt 5.13.0, in which "wasm_32" is a valid arch for Linux but not win/mac."""
# Generate some mock Updates.xml files. The host mismatch is not important; we just want to check for arch==wasm
updates_xmltext, wasm_updates_xmltext = [
(Path(__file__).parent / "data" / filename).read_text("utf-8").replace("qt5.5140", "qt5.5130")
for filename in ("windows-5140-update.xml", "windows-5140-wasm-update.xml")
]
def _mock_fetch_http(_, rest_of_url, *args, **kwargs: str) -> str:
if rest_of_url == "online/qtsdkrepository/linux_x64/desktop/qt5_5130_wasm/Updates.xml":
return wasm_updates_xmltext
elif rest_of_url.endswith("/desktop/qt5_5130/Updates.xml"):
return updates_xmltext
else:
assert False, f"Fetched an unexpected file at '{rest_of_url}'"
monkeypatch.setattr(MetadataFactory, "fetch_http", _mock_fetch_http)
cli = Cli()
cli.run(["list-qt", host, "desktop", "--arch", "5.13.0"])
out, err = capsys.readouterr()
output_set = set(out.strip().split())
if has_wasm:
assert "wasm_32" in output_set
else:
assert "wasm_32" not in output_set
@pytest.mark.parametrize(
"cmd, host, expect",
(
("list-qt", "windows", {"desktop", "android", "winrt"}),
("list-qt", "linux", {"desktop", "android"}),
("list-qt", "mac", {"desktop", "android", "ios"}),
("list-tool", "windows", {"desktop", "android", "winrt"}),
("list-tool", "linux", {"desktop", "android"}),
("list-tool", "mac", {"desktop", "android", "ios"}),
),
)
def test_list_targets(capsys, cmd: str, host: str, expect: Set[str]):
cli = Cli()
cli.run([cmd, host])
out, err = capsys.readouterr()
output_set = set(out.strip().split())
assert output_set == expect
@pytest.mark.parametrize(
"cmd, host, target",
(
("list-qt", "windows", "ios"),
("list-qt", "linux", "ios"),
("list-qt", "linux", "winrt"),
("list-qt", "mac", "winrt"),
("list-tool", "windows", "ios"),
("list-tool", "linux", "ios"),
("list-tool", "linux", "winrt"),
("list-tool", "mac", "winrt"),
),
)
def test_list_wrong_target(capsys, cmd: str, host: str, target: str):
expect = f"ERROR : '{target}' is not a valid target for host '{host}'"
cli = Cli()
return_code = cli.run([cmd, host, target])
out, err = capsys.readouterr()
assert return_code == 1
assert err.strip() == expect
@pytest.mark.parametrize(
"cmd, spec",
(
("list-qt", "not a spec"),
("list-qt", "1...3"),
("list-qt", ""),
("list-qt", ">3 <5"),
),
)
def test_invalid_spec(capsys, cmd: str, spec: str):
expect_prefix = f"ERROR : Invalid version specification: '{spec}'"
host, target = "linux", "desktop"
cli = Cli()
return_code = cli.run([cmd, host, target, "--spec", spec])
out, err = capsys.readouterr()
assert return_code == 1
assert err.strip().startswith(expect_prefix)
@pytest.mark.parametrize(
"simple_spec, expected_name",
(
(SimpleSpec("*"), "mytool.999"),
(SimpleSpec(">3.5"), "mytool.999"),
(SimpleSpec("3.5.5"), "mytool.355"),
(SimpleSpec("<3.5"), "mytool.300"),
(SimpleSpec("<=3.5"), "mytool.355"),
(SimpleSpec("<=3.5.0"), "mytool.350"),
(SimpleSpec(">10"), None),
),
)
def test_list_choose_tool_by_version(simple_spec, expected_name):
tools_data = {
"mytool.999": {"Version": "9.9.9", "Name": "mytool.999"},
"mytool.355": {"Version": "3.5.5", "Name": "mytool.355"},
"mytool.350": {"Version": "3.5.0", "Name": "mytool.350"},
"mytool.300": {"Version": "3.0.0", "Name": "mytool.300"},
}
item = MetadataFactory.choose_highest_version_in_spec(tools_data, simple_spec)
if item is not None:
assert item["Name"] == expected_name
else:
assert expected_name is None
mac_qt = ArchiveId("qt", "mac", "desktop")
wrong_tool_name_msg = "Please use 'aqt list-tool mac desktop' to check what tools are available."
wrong_qt_version_msg = "Please use 'aqt list-qt mac desktop' to show versions of Qt available."
wrong_arch_msg = "Please use 'aqt list-qt mac desktop --arch <QT_VERSION>' to list valid architectures."
@pytest.mark.parametrize(
"meta, expected_message",
(
(MetadataFactory(mac_qt), []),
(
MetadataFactory(mac_qt, spec=SimpleSpec("5.0")),
["Please use 'aqt list-qt mac desktop' to check that versions of qt exist within the spec '5.0'."],
),
(
MetadataFactory(ArchiveId("tools", "mac", "desktop"), tool_name="ifw"),
[wrong_tool_name_msg],
),
(
MetadataFactory(mac_qt, architectures_ver="1.2.3"),
[wrong_qt_version_msg],
),
(
MetadataFactory(mac_qt, modules_query=ModulesQuery("1.2.3", "clang_64")),
[wrong_qt_version_msg, wrong_arch_msg],
),
(
MetadataFactory(mac_qt, archives_query=["1.2.3", "clang_64", "a module", "another module"]),
[
"Please use 'aqt list-qt mac desktop' to show versions of Qt available.",
"Please use 'aqt list-qt mac desktop --arch <QT_VERSION>' to show architectures available.",
"Please use 'aqt list-qt mac desktop --modules <QT_VERSION>' to show modules available.",
],
),
(
MetadataFactory(mac_qt, archives_query=["1.2.3", "clang_64"]),
[
"Please use 'aqt list-qt mac desktop' to show versions of Qt available.",
"Please use 'aqt list-qt mac desktop --arch <QT_VERSION>' to show architectures available.",
],
),
(
MetadataFactory(mac_qt, spec=SimpleSpec("<5.9")),
["Please use 'aqt list-qt mac desktop' to check that versions of qt exist within the spec '<5.9'."],
),
(
MetadataFactory(ArchiveId("tools", "mac", "desktop"), tool_name="ifw"),
[wrong_tool_name_msg],
),
(
MetadataFactory(mac_qt, architectures_ver="1.2.3"),
[wrong_qt_version_msg],
),
(
MetadataFactory(mac_qt, modules_query=ModulesQuery("1.2.3", "clang_64")),
[wrong_qt_version_msg, wrong_arch_msg],
),
),
)
def test_suggested_follow_up(meta: MetadataFactory, expected_message: str):
assert suggested_follow_up(meta) == expected_message
def test_format_suggested_follow_up():
suggestions = [
"Please use 'aqt list-tool mac desktop --modules <QT_VERSION>' to list valid modules.",
"Please use 'aqt list-tool mac desktop' to check what tools are available.",
]
expected = (
"==============================Suggested follow-up:==============================\n"
"* Please use 'aqt list-tool mac desktop --modules <QT_VERSION>' to list valid modules.\n"
"* Please use 'aqt list-tool mac desktop' to check what tools are available."
)
ex = AqtException("msg", suggested_action=suggestions)
assert ex._format_suggested_follow_up() == expected
def test_format_suggested_follow_up_empty():
ex = AqtException("msg", suggested_action=[])
assert format(ex) == "msg"
@pytest.mark.parametrize(
"meta, expect",
(
(
MetadataFactory(ArchiveId("qt", "mac", "desktop"), spec=SimpleSpec("5.42")),
"qt/mac/desktop with spec 5.42",
),
(
MetadataFactory(ArchiveId("qt", "mac", "desktop"), spec=SimpleSpec("5.42")),
"qt/mac/desktop with spec 5.42",
),
(MetadataFactory(ArchiveId("qt", "mac", "desktop")), "qt/mac/desktop"),
(
MetadataFactory(ArchiveId("qt", "mac", "desktop")),
"qt/mac/desktop",
),
),
)
def test_list_describe_filters(meta: MetadataFactory, expect: str):
assert meta.describe_filters() == expect
@pytest.mark.parametrize(
"archive_id, spec, version_str, arch, expect",
(
(mac_qt, None, "5.12.42", None, Version("5.12.42")),
(
mac_qt,
None,
"not a version",
None,
CliInputError("Invalid version string: 'not a version'"),
),
(mac_qt, SimpleSpec("5"), "latest", None, Version("5.15.2")),
(
mac_qt,
SimpleSpec("5.0"),
"latest",
None,
CliInputError("There is no latest version of Qt with the criteria 'qt/mac/desktop with spec 5.0'"),
),
(mac_qt, SimpleSpec("<6.2.0"), "latest", "wasm_32", Version("5.15.2")),
),
)
def test_list_to_version(monkeypatch, archive_id, spec, version_str, arch: Optional[str], expect):
_html = (Path(__file__).parent / "data" / "mac-desktop.html").read_text("utf-8")
monkeypatch.setattr(MetadataFactory, "fetch_http", lambda *args, **kwargs: _html)
if isinstance(expect, Exception):
with pytest.raises(CliInputError) as error:
MetadataFactory(archive_id, spec=spec)._to_version(version_str, arch)
assert error.type == CliInputError
assert str(expect) == str(error.value)
else:
assert MetadataFactory(archive_id, spec=spec)._to_version(version_str, arch) == expect
def test_list_fetch_tool_by_simple_spec(monkeypatch):
update_xml = (Path(__file__).parent / "data" / "windows-desktop-tools_vcredist-update.xml").read_text("utf-8")
monkeypatch.setattr(MetadataFactory, "fetch_http", lambda self, _: update_xml)
expect_json = (Path(__file__).parent / "data" / "windows-desktop-tools_vcredist-expect.json").read_text("utf-8")
expected = json.loads(expect_json)["modules_data"]
def check(actual, expect):
for key in (
"Description",
"DisplayName",
"DownloadableArchives",
"ReleaseDate",
"SHA1",
"Version",
"Virtual",
):
assert actual[key] == expect[key]
meta = MetadataFactory(ArchiveId("tools", "windows", "desktop"))
check(
meta.fetch_tool_by_simple_spec(tool_name="tools_vcredist", simple_spec=SimpleSpec("2011")),
expected["qt.tools.vcredist"],
)
check(
meta.fetch_tool_by_simple_spec(tool_name="tools_vcredist", simple_spec=SimpleSpec("2014")),
expected["qt.tools.vcredist_msvc2013_x86"],
)
nonexistent = meta.fetch_tool_by_simple_spec(tool_name="tools_vcredist", simple_spec=SimpleSpec("1970"))
assert nonexistent is None
# Simulate a broken Updates.xml file, with invalid versions
highest_module_info = MetadataFactory.choose_highest_version_in_spec(
all_tools_data={"some_module": {"Version": "not_a_version"}},
simple_spec=SimpleSpec("*"),
)
assert highest_module_info is None
@pytest.mark.parametrize(
"columns, expect",
(
(
120,
(
"Tool Variant Name Version Release Date Display Name "
" Description \n"
"====================================================================================="
"===================================\n"
"qt.tools.ifw.41 4.1.1-202105261132 2021-05-26 Qt Installer Framework 4.1 "
"The Qt Installer Framework provides\n"
" "
"a set of tools and utilities to \n"
" "
"create installers for the supported\n"
" "
"desktop Qt platforms: Linux, \n"
" "
"Microsoft Windows, and macOS. \n"
),
),
(
80,
"Tool Variant Name Version Release Date\n"
"=====================================================\n"
"qt.tools.ifw.41 4.1.1-202105261132 2021-05-26 \n",
),
(
0,
"Tool Variant Name Version Release Date Display Name "
" Descriptio"
"n \n"
"====================================================================================="
"====================================================================================="
"=============================================================================\n"
"qt.tools.ifw.41 4.1.1-202105261132 2021-05-26 Qt Installer Framework 4.1 "
"The Qt Installer Framework provides a set of tools and utilities to create installers"
" for the supported desktop Qt platforms: Linux, Microsoft Windows, and macOS.\n",
),
),
)
def test_show_list_tools_long_ifw(capsys, monkeypatch, columns, expect):
update_xml = (Path(__file__).parent / "data" / "mac-desktop-tools_ifw-update.xml").read_text("utf-8")
monkeypatch.setattr(MetadataFactory, "fetch_http", lambda self, _: update_xml)
monkeypatch.setattr(shutil, "get_terminal_size", lambda fallback: os.terminal_size((columns, 24)))
meta = MetadataFactory(
ArchiveId("tools", "mac", "desktop"),
tool_name="tools_ifw",
is_long_listing=True,
)
show_list(meta)
out, err = capsys.readouterr()
sys.stdout.write(out)
sys.stderr.write(err)
assert out == expect
LONG_MODULES_WIN_5140_120 = (
" Module Name Display Name Release Date Download Size Installed Size\n"
"====================================================================================================================\n"
"qtcharts Qt Charts for MinGW 7.3.0 32-bit 2019-12-11 772.7K 7.8M \n"
"qtdatavis3d Qt Data Visualization for MinGW 7.3.0 32-bit 2019-12-11 620.2K 5.0M \n"
"qtlottie Qt Lottie Animation for MinGW 7.3.0 32-bit 2019-12-11 153.5K 968.4K \n"
"qtnetworkauth Qt Network Authorization for MinGW 7.3.0 32-bit 2019-12-11 98.7K 638.6K \n"
"qtpurchasing Qt Purchasing for MinGW 7.3.0 32-bit 2019-12-11 50.9K 306.8K \n"
"qtquick3d Qt Quick 3D for MinGW 7.3.0 32-bit 2019-12-11 9.8M 21.2M \n"
"qtquicktimeline Qt Quick Timeline for MinGW 7.3.0 32-bit 2019-12-11 35.3K 154.1K \n"
"qtscript Qt Script for MinGW 7.3.0 32-bit 2019-12-11 1.0M 5.5M \n"
"qtvirtualkeyboard Qt Virtual Keyboard for MinGW 7.3.0 32-bit 2019-12-11 2.1M 6.8M \n"
"qtwebglplugin Qt WebGL Streaming Plugin for MinGW 7.3.0 32-bit 2019-12-11 201.7K 1.0M \n"
)
LONG_MODULES_WIN_5140_80 = (
" Module Name Display Name \n"
"====================================================================\n"
"qtcharts Qt Charts for MinGW 7.3.0 32-bit \n"
"qtdatavis3d Qt Data Visualization for MinGW 7.3.0 32-bit \n"
"qtlottie Qt Lottie Animation for MinGW 7.3.0 32-bit \n"
"qtnetworkauth Qt Network Authorization for MinGW 7.3.0 32-bit \n"
"qtpurchasing Qt Purchasing for MinGW 7.3.0 32-bit \n"
"qtquick3d Qt Quick 3D for MinGW 7.3.0 32-bit \n"
"qtquicktimeline Qt Quick Timeline for MinGW 7.3.0 32-bit \n"
"qtscript Qt Script for MinGW 7.3.0 32-bit \n"
"qtvirtualkeyboard Qt Virtual Keyboard for MinGW 7.3.0 32-bit \n"
"qtwebglplugin Qt WebGL Streaming Plugin for MinGW 7.3.0 32-bit\n"
)
@pytest.mark.parametrize(
"columns, use_cli, expect",
(
(120, False, LONG_MODULES_WIN_5140_120),
(80, False, LONG_MODULES_WIN_5140_80),
(120, True, LONG_MODULES_WIN_5140_120),
(80, True, LONG_MODULES_WIN_5140_80),
),
)
def test_show_list_long_qt_modules(capsys, monkeypatch, columns: int, use_cli: bool, expect: str):
update_xml = (Path(__file__).parent / "data" / "windows-5140-update.xml").read_text("utf-8")
monkeypatch.setattr(MetadataFactory, "fetch_http", lambda self, _: update_xml)
cli = Cli()
# Patching get_terminal_size prevents successful instantiation of Cli, so do it afterwards:
monkeypatch.setattr(shutil, "get_terminal_size", lambda fallback: os.terminal_size((columns, 24)))
if use_cli:
return_code = cli.run("list-qt windows desktop --long-modules 5.14.0 win32_mingw73".split())
assert return_code == 0
else:
meta = MetadataFactory(
ArchiveId("qt", "windows", "desktop"),
modules_query=ModulesQuery("5.14.0", "win32_mingw73"),
is_long_listing=True,
)
show_list(meta)
out, err = capsys.readouterr()
sys.stdout.write(out)
sys.stderr.write(err)
assert out == expect
def test_show_list_versions(monkeypatch, capsys):
_html = (Path(__file__).parent / "data" / "mac-desktop.html").read_text("utf-8")
monkeypatch.setattr(MetadataFactory, "fetch_http", lambda *args: _html)
expect_file = Path(__file__).parent / "data" / "mac-desktop-expect.json"
expected = "\n".join(json.loads(expect_file.read_text("utf-8"))["qt"]["qt"]) + "\n"
show_list(MetadataFactory(mac_qt))
out, err = capsys.readouterr()
assert out == expected
def test_show_list_tools(monkeypatch, capsys):
page = (Path(__file__).parent / "data" / "mac-desktop.html").read_text("utf-8")
monkeypatch.setattr(MetadataFactory, "fetch_http", lambda *args, **kwargs: page)
expect_file = Path(__file__).parent / "data" / "mac-desktop-expect.json"
expect = "\n".join(json.loads(expect_file.read_text("utf-8"))["tools"]) + "\n"
meta = MetadataFactory(ArchiveId("tools", "mac", "desktop"))
show_list(meta)
out, err = capsys.readouterr()
sys.stdout.write(out)
sys.stderr.write(err)
assert out == expect
def test_show_list_empty(monkeypatch, capsys):
monkeypatch.setattr(MetadataFactory, "getList", lambda self: [])
meta = MetadataFactory(ArchiveId("tools", "mac", "desktop"))
with pytest.raises(EmptyMetadata) as error:
show_list(meta)
assert error.type == EmptyMetadata
assert format(error.value) == "No tools available for this request."
@pytest.mark.parametrize(
"exception_class, error_msg, source",
(
(ArchiveConnectionError, "Failure to connect to some url", "aqt.metadata.getUrl"),
(ArchiveDownloadError, "Failure to download some xml file", "aqt.metadata.getUrl"),
),
)
def test_show_list_bad_connection(monkeypatch, capsys, exception_class, error_msg, source):
def mock(*args, **kwargs):
raise exception_class(error_msg)
monkeypatch.setattr(source, mock)
meta = MetadataFactory(mac_qt, spec=SimpleSpec("<5.9"))
with pytest.raises(exception_class) as error:
show_list(meta)
assert error.type == exception_class
assert format(error.value) == (
f"{error_msg}\n"
"==============================Suggested follow-up:==============================\n"
"* Please use 'aqt list-qt mac desktop' to check that versions of qt exist within the spec '<5.9'."
)
@pytest.mark.parametrize(
"exception_class, error_msg, source", ((ArchiveConnectionError, "Failure to connect", "aqt.helper.getUrl"),)
)
def test_show_list_bad_connection_for_checksum(monkeypatch, capsys, exception_class, error_msg, source):
def mock(*args, **kwargs):
raise exception_class(error_msg)
monkeypatch.setattr(source, mock)
cli = Cli()
cli.run("list-qt linux desktop --arch 6.4.0".split())
out, err = capsys.readouterr()
sys.stdout.write(out)
sys.stderr.write(err)
assert (
err == "ERROR : Failed to download checksum for the file 'Updates.xml' from mirrors "
"'['https://download.qt.io']\n"
"==============================Suggested follow-up:==============================\n"
"* Check your internet connection\n"
"* Consider modifying `requests.max_retries_to_retrieve_hash` in settings.ini\n"
"* Consider modifying `mirrors.trusted_mirrors` in settings.ini "
"(see https://aqtinstall.readthedocs.io/en/stable/configuration.html#configuration)\n"
"* Please use 'aqt list-qt linux desktop' to show versions of Qt available.\n"
)
def fetch_expected_tooldata(json_filename: str) -> ToolData:
text = (Path(__file__).parent / "data" / json_filename).read_text("utf-8")
raw_tooldata: List[List[str]] = json.loads(text)["long_listing"]
keys = ("Version", "ReleaseDate", "DisplayName", "Description")
tools: Dict[str, Dict[str, str]] = {}
for variant_name, *values in raw_tooldata:
assert len(keys) == len(values)
tools[variant_name] = {k: v for k, v in zip(keys, values)}
return ToolData(tools)
@pytest.mark.parametrize(
"host, target, tool_name",
(
("mac", "desktop", "tools_cmake"),
("mac", "desktop", "sdktool"),
),
)
def test_list_tool_cli(monkeypatch, capsys, host: str, target: str, tool_name: str):
html_file = f"{host}-{target}.html"
xml_file = f"{host}-{target}-{tool_name}-update.xml"
html_expect = f"{host}-{target}-expect.json"
xml_expect = f"{host}-{target}-{tool_name}-expect.json"
htmltext, xmltext, htmljson, xmljson = [
(Path(__file__).parent / "data" / filename).read_text("utf-8")
for filename in (html_file, xml_file, html_expect, xml_expect)
]
expected_tools = set(json.loads(htmljson)["tools"])
xml_data = json.loads(xmljson)
expected_tool_modules = set(xml_data["modules"])
def _mock_fetch_http(_, rest_of_url, *args, **kwargs: str) -> str:
if not rest_of_url.endswith("Updates.xml"):
return htmltext
folder = urlparse(rest_of_url).path.split("/")[-2]
assert folder.startswith("tools_") or folder in ["sdktool"]
return xmltext
monkeypatch.setattr(MetadataFactory, "fetch_http", _mock_fetch_http)
cli = Cli()
cli.run(["list-tool", host, target])
out, err = capsys.readouterr()
assert not err
output_set = set(out.strip().split())
assert output_set == expected_tools
cli.run(["list-tool", host, target, tool_name])
out, err = capsys.readouterr()
assert not err
output_set = set(out.strip().split())
assert output_set == expected_tool_modules
# Test abbreviated tool name: "aqt list-tool mac desktop ifw"
assert tool_name.startswith("tools_") or tool_name in ["sdktool"]
short_tool_name = tool_name[6:] if tool_name.startswith("tools_") else tool_name
cli.run(["list-tool", host, target, short_tool_name])
out, err = capsys.readouterr()
assert not err
output_set = set(out.strip().split())
assert output_set == expected_tool_modules
cli.run(["list-tool", host, target, tool_name, "-l"])
out, err = capsys.readouterr()
assert not err
expected_tooldata = format(fetch_expected_tooldata(xml_expect))
assert out.strip() == expected_tooldata.strip()
def test_fetch_http_ok(monkeypatch):
html_content = b"some_html_content"
base_url = "https://alt.baseurl.com"
def mock_getUrl(url: str, *args, **kwargs) -> str:
assert url.startswith(base_url)
return str(html_content)
monkeypatch.setattr("aqt.metadata.get_hash", lambda *args, **kwargs: hashlib.sha256(html_content).hexdigest())
monkeypatch.setattr("aqt.metadata.getUrl", mock_getUrl)
assert MetadataFactory(mac_qt, base_url=base_url).fetch_http("some_url") == str(html_content)
def test_fetch_http_failover(monkeypatch):
urls_requested = set()
def _mock(url, **kwargs):
urls_requested.add(url)
if len(urls_requested) <= 1:
raise ArchiveDownloadError()
return "some_html_content"
monkeypatch.setattr("aqt.metadata.get_hash", lambda *args, **kwargs: hashlib.sha256(b"some_html_content").hexdigest())
monkeypatch.setattr("aqt.metadata.getUrl", _mock)
# Require that the first attempt failed, but the second did not
assert MetadataFactory(mac_qt).fetch_http("some_url") == "some_html_content"
assert len(urls_requested) == 2
@pytest.mark.parametrize("exception_on_error", (ArchiveDownloadError, ArchiveConnectionError))
def test_fetch_http_download_error(monkeypatch, exception_on_error):
urls_requested = set()
def _mock(url, **kwargs):
urls_requested.add(url)
raise exception_on_error()
monkeypatch.setattr("aqt.metadata.get_hash", lambda *args, **kwargs: hashlib.sha256(b"some_html_content").hexdigest())
monkeypatch.setattr("aqt.metadata.getUrl", _mock)
with pytest.raises(exception_on_error) as e:
MetadataFactory(mac_qt).fetch_http("some_url")
assert e.type == exception_on_error
# Require that a fallback url was tried
assert len(urls_requested) == 2
@pytest.mark.parametrize(
"host, expected, all_arches",
(
("windows", "win32_mingw", ["win64_msvc2013_64", "win32_mingw", "win16_mingw"]),
("windows", "win32_mingw", ["win64_msvc2013_64", "win32_mingw"]),
("windows", "win1_mingw0", ["win64_msvc2013_64", "win_mingw900", "win1_mingw0"]),
("windows", "win32_mingw1", ["win64_msvc2013_64", "win32_mingw", "win32_mingw1"]),
("windows", "win64_mingw", ["win64_msvc2013_64", "win32_mingw900", "win64_mingw"]),
("windows", "win64_mingw81", ["win64_msvc2013_64", "win64_mingw81", "win64_mingw"]),
("windows", "win64_mingw73", ["win64_msvc2013_64", "win64_mingw53", "win64_mingw73"]),
("windows", "win64_mingw", ["win64_msvc2013_64", "win64_mingw", "win64_mingw"]),
("windows", EmptyMetadata(), []),
("linux", "gcc_64", ["should not fetch arches"]),
("mac", "clang_64", ["should not fetch arches"]),
),
)
def test_select_default_mingw(monkeypatch, host: str, expected: Union[str, Exception], all_arches: List[str]):
monkeypatch.setattr("aqt.metadata.MetadataFactory.fetch_arches", lambda *args, **kwargs: all_arches)
if isinstance(expected, Exception):
with pytest.raises(type(expected)) as e:
MetadataFactory(ArchiveId("qt", host, "desktop")).fetch_default_desktop_arch(Version("1.2.3"))
assert e.type == type(expected)
else:
actual_arch = MetadataFactory(ArchiveId("qt", host, "desktop")).fetch_default_desktop_arch(Version("1.2.3"))
assert actual_arch == expected
@pytest.mark.parametrize(
"expected_result, installed_files",
(
("mingw73_32", ["mingw73_32/bin/qmake.exe", "msvc2017/bin/qmake.exe"]),
(None, ["msvc2017/bin/qmake.exe"]),
(None, ["mingw73_win/bin/qmake.exe"]), # Bad directory: mingw73_win does not fit the mingw naming convention
(None, ["mingw73_32/bin/qmake", "msvc2017/bin/qmake.exe"]),
("mingw81_32", ["mingw73_32/bin/qmake.exe", "mingw81_32/bin/qmake.exe"]),
("mingw73_64", ["mingw73_64/bin/qmake.exe", "mingw73_32/bin/qmake.exe"]),
),
)
def test_find_installed_qt_mingw_dir(expected_result: str, installed_files: List[str]):
qt_ver = "6.3.0"
host = "windows"
# Setup a mock install directory that includes some installed files
with TemporaryDirectory() as base_dir:
base_path = Path(base_dir)
for file in installed_files:
path = base_path / qt_ver / file
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("Mock installed file")
actual_result = QtRepoProperty.find_installed_desktop_qt_dir(host, base_path, Version(qt_ver))
assert (actual_result.name if actual_result else None) == expected_result