Skip to content
This repository has been archived by the owner on Oct 15, 2024. It is now read-only.

Commit

Permalink
test: test Python scripts against different Qt dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
seanwu1105 committed Mar 3, 2023
1 parent b1aec1e commit adf7eb1
Show file tree
Hide file tree
Showing 17 changed files with 230 additions and 111 deletions.
2 changes: 1 addition & 1 deletion python/.pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ persistent=yes

# Minimum Python version to use for version dependent checks. Will default to
# the version used to run pylint.
py-version=3.9
py-version=3.10

# Discover python modules and packages in the file system subtree.
recursive=no
Expand Down
1 change: 1 addition & 0 deletions python/.vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"[python]": {
"editor.defaultFormatter": "ms-python.python"
},
"python.analysis.autoImportCompletions": true,
"python.formatting.provider": "black",
"python.linting.enabled": true,
"python.linting.pylintEnabled": true,
Expand Down
12 changes: 9 additions & 3 deletions python/scripts/designer.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
# pylint: disable=import-error
# pylint: disable=import-error,ungrouped-imports

import sys

from utils import is_installed
from utils import is_installed, parse_qt_dependency

if __name__ == "__main__":
if is_installed("PySide6"):
dep = parse_qt_dependency()
if dep == "PySide6":
from PySide6.scripts.pyside_tool import designer
elif dep == "PySide2":
from PySide2.scripts.pyside_tool import designer

elif is_installed("PySide6"):
from PySide6.scripts.pyside_tool import designer
elif is_installed("PySide2"):
from PySide2.scripts.pyside_tool import designer
Expand Down
17 changes: 14 additions & 3 deletions python/scripts/lupdate.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
# pylint: disable=import-error
# pylint: disable=import-error,ungrouped-imports

import sys

from utils import is_installed
from utils import is_installed, parse_qt_dependency

if __name__ == "__main__":
if is_installed("PySide6"):
dep = parse_qt_dependency()
if dep == "PySide6":
from PySide6.scripts.pyside_tool import lupdate
elif dep == "PySide2":
sys.argv[0] = "pyside2-lupdate"
from PySide2.scripts.pyside_tool import main as lupdate
elif dep == "PyQt6":
from PyQt6.lupdate.pylupdate import main as lupdate
elif dep == "PyQt5":
from PyQt5.pylupdate_main import main as lupdate

elif is_installed("PySide6"):
from PySide6.scripts.pyside_tool import lupdate
elif is_installed("PySide2"):
sys.argv[0] = "pyside2-lupdate"
Expand Down
10 changes: 7 additions & 3 deletions python/scripts/qml.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
# pylint: disable=import-error
# pylint: disable=import-error,ungrouped-imports

import sys

from utils import is_installed
from utils import is_installed, parse_qt_dependency

if __name__ == "__main__":
if is_installed("PySide6"):
dep = parse_qt_dependency()
if dep == "PySide6":
from PySide6.scripts.pyside_tool import qml

elif is_installed("PySide6"):
from PySide6.scripts.pyside_tool import qml
else:
sys.exit("No pyside6-qml can be found in current Python environment.")
Expand Down
8 changes: 6 additions & 2 deletions python/scripts/qmlls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@

import sys

from utils import is_installed
from utils import is_installed, parse_qt_dependency

if __name__ == "__main__":
if is_installed("PySide6"):
dep = parse_qt_dependency()
if dep == "PySide6":
from PySide6.scripts.pyside_tool import qmlls

elif is_installed("PySide6"):
from PySide6.scripts.pyside_tool import qmlls
else:
sys.exit("No qmlls can be found in current Python environment.")
Expand Down
14 changes: 11 additions & 3 deletions python/scripts/rcc.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
# pylint: disable=import-error
# pylint: disable=import-error,ungrouped-imports

import sys

from utils import is_installed
from utils import is_installed, parse_qt_dependency

if __name__ == "__main__":
if is_installed("PySide6"):
dep = parse_qt_dependency()
if dep == "PySide6":
from PySide6.scripts.pyside_tool import rcc
elif dep == "PySide2":
from PySide2.scripts.pyside_tool import rcc
elif dep == "PyQt5":
from PyQt5.pyrcc_main import main as rcc

elif is_installed("PySide6"):
from PySide6.scripts.pyside_tool import rcc
elif is_installed("PySide2"):
from PySide2.scripts.pyside_tool import rcc
Expand Down
16 changes: 13 additions & 3 deletions python/scripts/uic.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
# pylint: disable=import-error
# pylint: disable=import-error,ungrouped-imports

import sys

from utils import is_installed
from utils import is_installed, parse_qt_dependency

if __name__ == "__main__":
if is_installed("PySide6"):
dep = parse_qt_dependency()
if dep == "PySide6":
from PySide6.scripts.pyside_tool import uic
elif dep == "PySide2":
from PySide2.scripts.pyside_tool import uic
elif dep == "PyQt6":
from PyQt6.uic.pyuic import main as uic
elif dep == "PyQt5":
from PyQt5.uic.pyuic import main as uic

elif is_installed("PySide6"):
from PySide6.scripts.pyside_tool import uic
elif is_installed("PySide2"):
from PySide2.scripts.pyside_tool import uic
Expand Down
23 changes: 23 additions & 0 deletions python/scripts/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,29 @@
import argparse
import importlib.util
import sys
import typing

QT_DEPENDENCY_ARG = "vscode_extension_qt_dependency"

SupportedQtDependencies = typing.Optional[
typing.Literal["PySide6", "PySide2", "PyQt6", "PyQt5"]
]


def is_installed(name: str) -> bool:
return name in sys.modules or importlib.util.find_spec(name) is not None


def parse_qt_dependency() -> SupportedQtDependencies:
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument(
f"--{QT_DEPENDENCY_ARG}",
required=False,
)

if dep := vars(parser.parse_known_args()[0])[QT_DEPENDENCY_ARG]:
sys.argv.remove(f"--{QT_DEPENDENCY_ARG}")
sys.argv.remove(dep)

return dep
return None
32 changes: 32 additions & 0 deletions python/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,39 @@
import os
import platform
import subprocess
import typing

from scripts.utils import QT_DEPENDENCY_ARG, SupportedQtDependencies

TESTS_DIR = os.path.dirname(os.path.realpath(__file__))

SCRIPTS_DIR = os.path.join(TESTS_DIR, os.pardir, "scripts")

ASSETS_DIR = os.path.join(TESTS_DIR, "assets")

SupportedScripts = typing.Literal["designer", "lupdate", "qml", "qmlls", "rcc", "uic"]


def filter_available_qt_dependencies(
deps: list[SupportedQtDependencies],
) -> list[SupportedQtDependencies]:
if platform.system() == "Darwin" and platform.machine() == "arm64":
return [None] + list(filter(lambda d: d in ("PySide2", "PyQt5"), deps))
return [None] + deps


def invoke_script(
name: SupportedScripts,
args: list[str],
qt_dependency: SupportedQtDependencies,
):
if qt_dependency is not None:
args.append(f"--{QT_DEPENDENCY_ARG}")
args.append(qt_dependency)

return subprocess.run(
["poetry", "run", "python", f"{name}.py", *args],
cwd=SCRIPTS_DIR,
capture_output=True,
check=False,
)
20 changes: 7 additions & 13 deletions python/tests/test_designer.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
import os
import subprocess

import pytest

from tests import ASSETS_DIR, SCRIPTS_DIR
from scripts.utils import SupportedQtDependencies
from tests import ASSETS_DIR, filter_available_qt_dependencies, invoke_script


@pytest.mark.skip(reason="a GUI app cannot be closed gracefully")
def test_designer_sample_ui():
@pytest.mark.parametrize(
"qt_dependency", filter_available_qt_dependencies(["PySide6", "PySide2"])
)
def test_designer_sample_ui(qt_dependency: SupportedQtDependencies):
filename = "sample.ui"
result = invoke_designer_py([get_assets_path(filename)])
result = invoke_script("designer", [get_assets_path(filename)], qt_dependency)
assert result.returncode == 0
assert len(result.stdout.decode("utf-8")) > 0


def invoke_designer_py(args: list[str]):
return subprocess.run(
["poetry", "run", "python", "designer.py", *args],
cwd=SCRIPTS_DIR,
capture_output=True,
check=True,
)


def get_assets_path(filename: str) -> str:
return os.path.join(ASSETS_DIR, "ui", filename)
49 changes: 31 additions & 18 deletions python/tests/test_lupdate.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,48 @@
import os
import subprocess

from tests import ASSETS_DIR, SCRIPTS_DIR
import pytest

from scripts.utils import SupportedQtDependencies
from tests import ASSETS_DIR, filter_available_qt_dependencies, invoke_script

def test_lupdate_help():
result = invoke_lupdate_py(["-help"])

@pytest.mark.parametrize(
"qt_dependency",
filter_available_qt_dependencies(["PySide6", "PySide2", "PyQt6", "PyQt5"]),
)
def test_lupdate_help(qt_dependency: SupportedQtDependencies):
help_arg = "--help" if qt_dependency == "PyQt6" else "-help"

result = invoke_script("lupdate", [help_arg], qt_dependency)
assert result.returncode == 0
assert len(result.stdout.decode("utf-8")) > 0

if qt_dependency in ("PySide2", "PyQt5"):
assert len(result.stderr.decode("utf-8")) > 0
else:
assert len(result.stdout.decode("utf-8")) > 0


@pytest.mark.parametrize(
"qt_dependency",
filter_available_qt_dependencies(["PySide6", "PySide2", "PyQt6", "PyQt5"]),
)
def test_lupdate_sample_py(qt_dependency: str):
try:
os.remove(get_assets_path("sample.ts"))
except FileNotFoundError:
pass

def test_lupdate_sample_py():
filename = "sample.py"
result = invoke_lupdate_py(
[get_assets_path(filename), "-ts", get_assets_path("sample.ts")]
result = invoke_script(
"lupdate",
[get_assets_path(filename), "-ts", get_assets_path("sample.ts")],
qt_dependency,
)
assert result.returncode == 0
assert len(result.stdout.decode("utf-8")) > 0
assert os.path.exists(get_assets_path("sample.ts"))

os.remove(get_assets_path("sample.ts"))


def invoke_lupdate_py(args: list[str]):
return subprocess.run(
["poetry", "run", "python", "lupdate.py", *args],
cwd=SCRIPTS_DIR,
capture_output=True,
check=True,
)


def get_assets_path(filename: str) -> str:
return os.path.join(ASSETS_DIR, "linguist", filename)
23 changes: 9 additions & 14 deletions python/tests/test_qml.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
import subprocess

import pytest
from tests import SCRIPTS_DIR

from scripts.utils import SupportedQtDependencies
from tests import filter_available_qt_dependencies, invoke_script


@pytest.mark.skip(reason="Ubuntu on GitHub Actions does not have libGL.so.1")
def test_qml_help():
result = invoke_qml_py(["--help"])
@pytest.mark.parametrize(
"qt_dependency",
filter_available_qt_dependencies(["PySide6"]),
)
def test_qml_help(qt_dependency: SupportedQtDependencies):
result = invoke_script("qml", ["--help"], qt_dependency)
assert result.returncode == 0
assert len(result.stdout.decode("utf-8")) > 0


def invoke_qml_py(args: list[str]):
return subprocess.run(
["poetry", "run", "python", "qml.py", *args],
cwd=SCRIPTS_DIR,
capture_output=True,
check=True,
)
22 changes: 9 additions & 13 deletions python/tests/test_qmlls.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
import subprocess
import pytest

from tests import SCRIPTS_DIR
from scripts.utils import SupportedQtDependencies
from tests import filter_available_qt_dependencies, invoke_script


def test_qmlls_help():
result = invoke_qmlls_py(["--help"])
@pytest.mark.parametrize(
"qt_dependency",
filter_available_qt_dependencies(["PySide6"]),
)
def test_qmlls_help(qt_dependency: SupportedQtDependencies):
result = invoke_script("qmlls", ["--help"], qt_dependency)
assert result.returncode == 0
assert len(result.stdout.decode("utf-8")) > 0


def invoke_qmlls_py(args: list[str]):
return subprocess.run(
["poetry", "run", "python", "qmlls.py", *args],
cwd=SCRIPTS_DIR,
capture_output=True,
check=True,
)
Loading

0 comments on commit adf7eb1

Please sign in to comment.