Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding ci test #12

Merged
merged 20 commits into from
Aug 21, 2024
31 changes: 31 additions & 0 deletions .github/workflows/test_plugin.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

name: Run tests

on:
push:
pull_request:
branches:
- 'main'

permissions:
contents: read

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Pull qgis image
run: docker pull qgis/qgis:stable

- name: Pip install
run: |
docker run --name qgis_container --volume $(pwd):/app -w=/app qgis/qgis:stable sh -c "python3 -m pip install pytest-qgis --break-system-packages"
docker commit qgis_container qgis_with_deps

- name: Run tests
run: docker run --volume $(pwd):/app -w=/app qgis_with_deps sh -c "xvfb-run -s '+extension GLX -screen 0 1024x768x24' python3 -m pytest tests -s"
88 changes: 53 additions & 35 deletions a00_qpip/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,18 @@
class Plugin:
"""QGIS Plugin Implementation."""

def __init__(self, iface):
def __init__(self, iface, plugin_path=None):
self.iface = iface
self._defered_packages = []
self.settings = QgsSettings()
self.settings.beginGroup("QPIP")

self.plugins_path = os.path.join(
QgsApplication.qgisSettingsDirPath(), "python", "plugins"
)
if plugin_path is None:
self.plugins_path = os.path.join(
QgsApplication.qgisSettingsDirPath(), "python", "plugins"
)
else:
self.plugins_path = plugin_path
self.prefix_path = os.path.join(
QgsApplication.qgisSettingsDirPath().replace("/", os.path.sep),
"python",
Expand Down Expand Up @@ -70,9 +73,10 @@ def initGui(self):
def initComplete(self):
if self._defered_packages:
log(f"Initialization complete. Loading deferred packages")
self.check_deps_and_prompt_install(
additional_plugins=self._defered_packages
)
dialog, run_gui = self.check_deps(additional_plugins=self._defered_packages)
if run_gui:
self.promt_install(dialog)
self.save_settings(dialog)
self.start_packages(self._defered_packages)
self._defered_packages = []

Expand All @@ -91,7 +95,9 @@ def unload(self):
os.environ["PYTHONPATH"] = os.environ["PYTHONPATH"].replace(
self.bin_path + os.pathsep, ""
)
os.environ["PATH"] = os.environ["PATH"].replace(self.bin_path + os.pathsep, "")
os.environ["PATH"] = os.environ["PATH"].replace(
self.bin_path + os.pathsep, ""
)

def patched_load_plugin(self, packageName):
"""
Expand All @@ -118,14 +124,21 @@ def patched_load_plugin(self, packageName):
return self._original_loadPlugin(packageName)
else:
log(f"Check on install enabled, we check {packageName}.")
self.check_deps_and_prompt_install(additional_plugins=[packageName])
dialog, run_gui = self.check_deps(additional_plugins=[packageName])
if run_gui:
self.promt_install(dialog)
self.save_settings(dialog)
self.start_packages([packageName])
return True

def check_deps_and_prompt_install(self, additional_plugins=[], force_gui=False):
def check_deps(self, additional_plugins=[]) -> MainDialog | bool:
"""
This checks dependencies for installed plugins and to-be installed plugins. If
anything is missing, shows a GUI to install them.

The function returns:
- MainDialog, the QDialog object (without opening it)
- A bool if the dialog needs to be opened or not
"""

plugin_names = [*qgis.utils.active_plugins, *additional_plugins]
Expand Down Expand Up @@ -165,31 +178,34 @@ def check_deps_and_prompt_install(self, additional_plugins=[], force_gui=False):
req = Req(plugin_name, str(requirement), error)
libs[requirement.key].name = requirement.key
libs[requirement.key].required_by.append(req)
dialog = MainDialog(
libs.values(), self._check_on_startup(), self._check_on_install()
)
return dialog, needs_gui

def promt_install(self, dialog: MainDialog):
"""Promts the install dialog and ask the user what to install"""
if dialog.exec_():
reqs_to_uninstall = dialog.reqs_to_uninstall
if reqs_to_uninstall:
log(f"Will uninstall selected dependencies : {reqs_to_uninstall}")
self.pip_uninstall_reqs(reqs_to_uninstall)

reqs_to_install = dialog.reqs_to_install
if reqs_to_install:
log(f"Will install selected dependencies : {reqs_to_install}")
self.pip_install_reqs(reqs_to_install)

def save_settings(self, dialog):
"""Stores the settings values"""
sys.path_importer_cache.clear()

if force_gui or needs_gui:
dialog = MainDialog(
libs.values(), self._check_on_startup(), self._check_on_install()
)
if dialog.exec_():
reqs_to_uninstall = dialog.reqs_to_uninstall
if reqs_to_uninstall:
log(f"Will uninstall selected dependencies : {reqs_to_uninstall}")
self.pip_uninstall_reqs(reqs_to_uninstall)

reqs_to_install = dialog.reqs_to_install
if reqs_to_install:
log(f"Will install selected dependencies : {reqs_to_install}")
self.pip_install_reqs(reqs_to_install)

sys.path_importer_cache.clear()

# Save these even if the dialog was closed
self.settings.setValue(
"check_on_startup", "yes" if dialog.check_on_startup else "no"
)
self.settings.setValue(
"check_on_install", "yes" if dialog.check_on_install else "no"
)
self.settings.setValue(
"check_on_startup", "yes" if dialog.check_on_startup else "no"
)
self.settings.setValue(
"check_on_install", "yes" if dialog.check_on_install else "no"
)

def start_packages(self, packageNames):
"""
Expand Down Expand Up @@ -249,7 +265,9 @@ def pip_install_reqs(self, reqs_to_install):
)

def check(self):
self.check_deps_and_prompt_install(force_gui=True)
dialog, _ = self.check_deps()
self.promt_install(dialog)
self.save_settings(dialog)

def show_folder(self):
if platform.system() == "Windows":
Expand Down
43 changes: 43 additions & 0 deletions tests/test_finding_req.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import os

import pytest

from a00_qpip.plugin import Plugin


class initializationCompleted:
def connect(self):
pass


def popWidget():
return True


THIS_DIR = os.path.dirname(__file__)


@pytest.fixture()
def plugin(qgis_iface):
qgis_iface.initializationCompleted = initializationCompleted
qgis_iface.messageBar().popWidget = popWidget
plugin = Plugin(qgis_iface, ".")
yield plugin


def test_plugin_a(plugin: Plugin):
plugin_a = os.path.join(THIS_DIR, "..", "test_plugins", "plugin_a")
dialog, needs_gui = plugin.check_deps([plugin_a])
libs = dialog.reqs_to_install
assert len(libs) == 2
assert libs[0] == "cowsay==4.0"
assert needs_gui


def test_plugin_b(plugin: Plugin):
plugin_b = os.path.join(THIS_DIR, "..", "test_plugins", "plugin_b")
dialog, needs_gui = plugin.check_deps([plugin_b])
libs = dialog.reqs_to_install
assert len(libs) == 2
assert libs[0] == "cowsay==5.0"
assert needs_gui
Loading