From e7e0f49c2545c98228cd9e8ba6a64f558e959345 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Fri, 7 Jun 2024 18:02:08 -0400 Subject: [PATCH 01/60] Update project layout closer to current standards --- AUTHORS | 1 + Makefile | 17 ++++- pyefis/instruments/__init__.py | 0 pyefis/version.py | 3 - pyproject.toml | 63 +++++++++++++++++++ setup.py | 61 ------------------ src/pyefis/__init__.py | 2 + {pyefis => src/pyefis}/cfg.py | 0 {pyefis => src/pyefis}/common/__init__.py | 0 .../config/buttons/auto-pilot-adjust.yaml | 0 .../buttons/auto-pilot-flight-plan.yaml | 0 .../buttons/auto-pilot-heading-hold.yaml | 0 .../config/buttons/baro-down-invisible.yaml | 0 .../config/buttons/baro-up-invisible.yaml | 0 .../config/buttons/dimmer-down-invisible.yaml | 0 .../config/buttons/dimmer-up-invisible.yaml | 0 .../pyefis}/config/buttons/egt-Lean.yaml | 0 .../pyefis}/config/buttons/egt-Normalize.yaml | 0 .../pyefis}/config/buttons/egt-Peak.yaml | 0 .../config/buttons/egt-reset-peak.yaml | 0 .../pyefis}/config/buttons/leader.yaml | 0 .../buttons/mgl/v16/active-rx-status.yaml | 0 .../buttons/mgl/v16/active-tx-status.yaml | 0 .../buttons/mgl/v16/standby-rx-status.yaml | 0 .../buttons/mgl/v16/swap-active-standby.yaml | 0 .../config/buttons/screen-android-pfd.yaml | 0 .../config/buttons/screen-ems-pfd.yaml | 0 .../config/buttons/screen-ems2-pfd.yaml | 0 .../config/buttons/screen-map-pfd.yaml | 0 .../config/buttons/screen-radio-pfd.yaml | 0 .../config/buttons/screen-sixpack-pfd.yaml | 0 .../config/buttons/trim-center-invisible.yaml | 0 .../config/buttons/trim-down-invisible.yaml | 0 .../buttons/trim-roll-center-invisible.yaml | 0 .../buttons/trim-roll-left-invisible.yaml | 0 .../buttons/trim-roll-right-invisible.yaml | 0 .../config/buttons/trim-up-invisible.yaml | 0 .../buttons/trim-yaw-center-invisible.yaml | 0 .../buttons/trim-yaw-left-invisible.yaml | 0 .../buttons/trim-yaw-right-invisible.yaml | 0 .../pyefis}/config/buttons/units.yaml | 0 .../pyefis}/config/databindings/default.yaml | 0 {pyefis => src/pyefis}/config/default.yaml | 0 .../pyefis}/config/examples/beagle.yaml | 0 .../pyefis}/config/examples/ems.yaml | 0 .../pyefis}/config/examples/rotax_ems.yaml | 0 .../pyefis}/config/hmi/encoder_input.yaml | 0 .../config/includes/ahrs/virtual_vfr.yaml | 0 ..._ds2_TACH1-OILT1-OAT-CURRNT-segmented.yaml | 0 ...HTOT1-VOLT_ds2_TACH1-OILT1-OAT-CURRNT.yaml | 0 ...ur_high_one_state_preferences_ARC9-12.yaml | 0 ...ur_high_two_states_preferences_ARC1-8.yaml | 0 .../power_temp_VOLT-CURRNT-OAT-CAT.yaml | 0 .../config/includes/bars/vertical/2_CHT.yaml | 0 .../config/includes/bars/vertical/2_EGT.yaml | 0 .../config/includes/bars/vertical/4_CHT.yaml | 0 .../config/includes/bars/vertical/4_EGT.yaml | 0 .../bars/vertical/dimmer_control.yaml | 0 ...FUELQT-FUELQ1-FUELQ2-FUELQ3-segmented.yaml | 0 ...P1-FUELF1-FUELQT-FUELQ1-FUELQ2-FUELQ3.yaml | 0 .../vertical/six_wide_preferences_BAR1-6.yaml | 0 ...-EMS-EMS2-ANDROID-RADIO-SIXPACK-Units.yaml | 0 ...n_changing_PFD-EMS-MAP-RADIO-AP-Units.yaml | 0 .../v16/radio-volumes-controls-display.yaml | 0 ...adio-volume-primary-secondary-display.yaml | 0 .../mgl/v16/square-active-radio-display.yaml | 0 .../mgl/v16/square-standby-radio-display.yaml | 0 .../trim/pitch-slider-button-control.yaml | 0 .../trim/pitch-yaw-roll-combined.yaml | 0 .../trim/roll-slider-button-control.yaml | 0 .../trim/yaw-slider-button-control.yaml | 0 .../pyefis}/config/keybindings/default.yaml | 0 .../pyefis}/config/lists/radio/favorites.yaml | 0 .../pyefis}/config/lists/radio/listbox.yaml | 0 .../pyefis}/config/lists/radio/ohio.yaml | 0 .../pyefis}/config/logging/file-debug.yaml | 0 .../pyefis}/config/logging/stderr-debug.yaml | 0 .../pyefis}/config/logging/stderr-info.yaml | 0 .../pyefis}/config/main/default.yaml | 0 .../pyefis}/config/outputs/default.yaml | 0 .../pyefis}/config/preferences.yaml | 0 .../pyefis}/config/preferences.yaml.custom | 0 .../config/screens/android-left-buttons.yaml | 0 .../pyefis}/config/screens/android.yaml | 0 .../pyefis}/config/screens/default_list.yaml | 0 .../config/screens/ems-left-buttons.yaml | 0 .../pyefis}/config/screens/ems.yaml | 0 .../config/screens/ems2-left-buttons.yaml | 0 .../pyefis}/config/screens/ems2.yaml | 0 .../config/screens/pfd-left-buttons.yaml | 0 .../pyefis}/config/screens/pfd.yaml | 0 .../config/screens/radio-left-buttons.yaml | 0 .../pyefis}/config/screens/radio.yaml | 0 .../config/screens/sixpack-left-buttons.yaml | 0 .../pyefis}/config/screens/sixpack.yaml | 0 .../pyefis}/config/screens/virtualvfr_db.yaml | 0 {pyefis => src/pyefis}/config/weston.ini | 0 {pyefis => src/pyefis}/gui.py | 0 {pyefis => src/pyefis}/hmi/__init__.py | 0 {pyefis => src/pyefis}/hmi/actionclass.py | 0 {pyefis => src/pyefis}/hmi/data.py | 0 {pyefis => src/pyefis}/hmi/functions.py | 0 {pyefis => src/pyefis}/hmi/keys.py | 0 {pyefis => src/pyefis}/hmi/menu.py | 0 {pyefis => src/pyefis}/hooks.py | 0 .../instruments/NumericalDisplay/__init__.py | 0 .../pyefis/instruments}/__init__.py | 0 .../pyefis}/instruments/ai/VirtualVfr.py | 0 .../pyefis}/instruments/ai/__init__.py | 0 .../pyefis}/instruments/airspeed/__init__.py | 0 .../pyefis}/instruments/altimeter/__init__.py | 0 .../pyefis}/instruments/button/__init__.py | 0 .../pyefis}/instruments/gauges/__init__.py | 0 .../pyefis}/instruments/gauges/abstract.py | 0 .../pyefis}/instruments/gauges/arc.py | 0 .../pyefis}/instruments/gauges/egt.py | 0 .../instruments/gauges/horizontalBar.py | 0 .../pyefis}/instruments/gauges/numeric.py | 0 .../pyefis}/instruments/gauges/verticalBar.py | 0 .../pyefis}/instruments/helpers/__init__.py | 0 .../pyefis}/instruments/hsi/__init__.py | 0 .../pyefis}/instruments/listbox/__init__.py | 0 .../pyefis}/instruments/misc/__init__.py | 0 .../pyefis}/instruments/pa/__init__.py | 0 .../pyefis}/instruments/tc/__init__.py | 0 .../pyefis}/instruments/vsi/__init__.py | 0 .../pyefis}/instruments/weston/__init__.py | 0 {pyefis => src/pyefis}/main.py | 0 {pyefis => src/pyefis}/screens/__init__.py | 0 {pyefis => src/pyefis}/screens/ems_sm.py | 0 {pyefis => src/pyefis}/screens/epfd.py | 0 {pyefis => src/pyefis}/screens/pfd.py | 0 {pyefis => src/pyefis}/screens/pfd_sm.py | 0 {pyefis => src/pyefis}/screens/r582_sm.py | 0 .../pyefis}/screens/screenbuilder.py | 0 {pyefis => src/pyefis}/screens/sixpack.py | 0 {pyefis => src/pyefis}/screens/test.py | 0 {pyefis => src/pyefis}/user/__init__.py | 0 {pyefis => src/pyefis}/user/hooks/__init__.py | 0 .../pyefis}/user/hooks/composite.py | 0 {pyefis => src/pyefis}/user/hooks/keys.py | 0 .../pyefis}/user/screens/__init__.py | 0 src/pyefis/version.py | 3 + 143 files changed, 85 insertions(+), 65 deletions(-) delete mode 100644 pyefis/instruments/__init__.py delete mode 100644 pyefis/version.py create mode 100644 pyproject.toml delete mode 100644 setup.py create mode 100644 src/pyefis/__init__.py rename {pyefis => src/pyefis}/cfg.py (100%) rename {pyefis => src/pyefis}/common/__init__.py (100%) rename {pyefis => src/pyefis}/config/buttons/auto-pilot-adjust.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/auto-pilot-flight-plan.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/auto-pilot-heading-hold.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/baro-down-invisible.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/baro-up-invisible.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/dimmer-down-invisible.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/dimmer-up-invisible.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/egt-Lean.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/egt-Normalize.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/egt-Peak.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/egt-reset-peak.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/leader.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/mgl/v16/active-rx-status.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/mgl/v16/active-tx-status.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/mgl/v16/standby-rx-status.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/mgl/v16/swap-active-standby.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/screen-android-pfd.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/screen-ems-pfd.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/screen-ems2-pfd.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/screen-map-pfd.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/screen-radio-pfd.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/screen-sixpack-pfd.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/trim-center-invisible.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/trim-down-invisible.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/trim-roll-center-invisible.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/trim-roll-left-invisible.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/trim-roll-right-invisible.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/trim-up-invisible.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/trim-yaw-center-invisible.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/trim-yaw-left-invisible.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/trim-yaw-right-invisible.yaml (100%) rename {pyefis => src/pyefis}/config/buttons/units.yaml (100%) rename {pyefis => src/pyefis}/config/databindings/default.yaml (100%) rename {pyefis => src/pyefis}/config/default.yaml (100%) rename {pyefis => src/pyefis}/config/examples/beagle.yaml (100%) rename {pyefis => src/pyefis}/config/examples/ems.yaml (100%) rename {pyefis => src/pyefis}/config/examples/rotax_ems.yaml (100%) rename {pyefis => src/pyefis}/config/hmi/encoder_input.yaml (100%) rename {pyefis => src/pyefis}/config/includes/ahrs/virtual_vfr.yaml (100%) rename {pyefis => src/pyefis}/config/includes/arcs/vertical/ds1_TACH1-OILP1-HTOT1-VOLT_ds2_TACH1-OILT1-OAT-CURRNT-segmented.yaml (100%) rename {pyefis => src/pyefis}/config/includes/arcs/vertical/ds1_TACH1-OILP1-HTOT1-VOLT_ds2_TACH1-OILT1-OAT-CURRNT.yaml (100%) rename {pyefis => src/pyefis}/config/includes/arcs/vertical/four_high_one_state_preferences_ARC9-12.yaml (100%) rename {pyefis => src/pyefis}/config/includes/arcs/vertical/four_high_two_states_preferences_ARC1-8.yaml (100%) rename {pyefis => src/pyefis}/config/includes/arcs/vertical/power_temp_VOLT-CURRNT-OAT-CAT.yaml (100%) rename {pyefis => src/pyefis}/config/includes/bars/vertical/2_CHT.yaml (100%) rename {pyefis => src/pyefis}/config/includes/bars/vertical/2_EGT.yaml (100%) rename {pyefis => src/pyefis}/config/includes/bars/vertical/4_CHT.yaml (100%) rename {pyefis => src/pyefis}/config/includes/bars/vertical/4_EGT.yaml (100%) rename {pyefis => src/pyefis}/config/includes/bars/vertical/dimmer_control.yaml (100%) rename {pyefis => src/pyefis}/config/includes/bars/vertical/fuel_info_FUELP1-FUELF1-FUELQT-FUELQ1-FUELQ2-FUELQ3-segmented.yaml (100%) rename {pyefis => src/pyefis}/config/includes/bars/vertical/fuel_info_FUELP1-FUELF1-FUELQT-FUELQ1-FUELQ2-FUELQ3.yaml (100%) rename {pyefis => src/pyefis}/config/includes/bars/vertical/six_wide_preferences_BAR1-6.yaml (100%) rename {pyefis => src/pyefis}/config/includes/buttons/vertical/screen_changing_PFD-EMS-EMS2-ANDROID-RADIO-SIXPACK-Units.yaml (100%) rename {pyefis => src/pyefis}/config/includes/buttons/vertical/screen_changing_PFD-EMS-MAP-RADIO-AP-Units.yaml (100%) rename {pyefis => src/pyefis}/config/includes/mgl/v16/radio-volumes-controls-display.yaml (100%) rename {pyefis => src/pyefis}/config/includes/mgl/v16/rectangle-radio-volume-primary-secondary-display.yaml (100%) rename {pyefis => src/pyefis}/config/includes/mgl/v16/square-active-radio-display.yaml (100%) rename {pyefis => src/pyefis}/config/includes/mgl/v16/square-standby-radio-display.yaml (100%) rename {pyefis => src/pyefis}/config/includes/trim/pitch-slider-button-control.yaml (100%) rename {pyefis => src/pyefis}/config/includes/trim/pitch-yaw-roll-combined.yaml (100%) rename {pyefis => src/pyefis}/config/includes/trim/roll-slider-button-control.yaml (100%) rename {pyefis => src/pyefis}/config/includes/trim/yaw-slider-button-control.yaml (100%) rename {pyefis => src/pyefis}/config/keybindings/default.yaml (100%) rename {pyefis => src/pyefis}/config/lists/radio/favorites.yaml (100%) rename {pyefis => src/pyefis}/config/lists/radio/listbox.yaml (100%) rename {pyefis => src/pyefis}/config/lists/radio/ohio.yaml (100%) rename {pyefis => src/pyefis}/config/logging/file-debug.yaml (100%) rename {pyefis => src/pyefis}/config/logging/stderr-debug.yaml (100%) rename {pyefis => src/pyefis}/config/logging/stderr-info.yaml (100%) rename {pyefis => src/pyefis}/config/main/default.yaml (100%) rename {pyefis => src/pyefis}/config/outputs/default.yaml (100%) rename {pyefis => src/pyefis}/config/preferences.yaml (100%) rename {pyefis => src/pyefis}/config/preferences.yaml.custom (100%) rename {pyefis => src/pyefis}/config/screens/android-left-buttons.yaml (100%) rename {pyefis => src/pyefis}/config/screens/android.yaml (100%) rename {pyefis => src/pyefis}/config/screens/default_list.yaml (100%) rename {pyefis => src/pyefis}/config/screens/ems-left-buttons.yaml (100%) rename {pyefis => src/pyefis}/config/screens/ems.yaml (100%) rename {pyefis => src/pyefis}/config/screens/ems2-left-buttons.yaml (100%) rename {pyefis => src/pyefis}/config/screens/ems2.yaml (100%) rename {pyefis => src/pyefis}/config/screens/pfd-left-buttons.yaml (100%) rename {pyefis => src/pyefis}/config/screens/pfd.yaml (100%) rename {pyefis => src/pyefis}/config/screens/radio-left-buttons.yaml (100%) rename {pyefis => src/pyefis}/config/screens/radio.yaml (100%) rename {pyefis => src/pyefis}/config/screens/sixpack-left-buttons.yaml (100%) rename {pyefis => src/pyefis}/config/screens/sixpack.yaml (100%) rename {pyefis => src/pyefis}/config/screens/virtualvfr_db.yaml (100%) rename {pyefis => src/pyefis}/config/weston.ini (100%) rename {pyefis => src/pyefis}/gui.py (100%) rename {pyefis => src/pyefis}/hmi/__init__.py (100%) rename {pyefis => src/pyefis}/hmi/actionclass.py (100%) rename {pyefis => src/pyefis}/hmi/data.py (100%) rename {pyefis => src/pyefis}/hmi/functions.py (100%) rename {pyefis => src/pyefis}/hmi/keys.py (100%) rename {pyefis => src/pyefis}/hmi/menu.py (100%) rename {pyefis => src/pyefis}/hooks.py (100%) rename {pyefis => src/pyefis}/instruments/NumericalDisplay/__init__.py (100%) rename {pyefis => src/pyefis/instruments}/__init__.py (100%) rename {pyefis => src/pyefis}/instruments/ai/VirtualVfr.py (100%) rename {pyefis => src/pyefis}/instruments/ai/__init__.py (100%) rename {pyefis => src/pyefis}/instruments/airspeed/__init__.py (100%) rename {pyefis => src/pyefis}/instruments/altimeter/__init__.py (100%) rename {pyefis => src/pyefis}/instruments/button/__init__.py (100%) rename {pyefis => src/pyefis}/instruments/gauges/__init__.py (100%) rename {pyefis => src/pyefis}/instruments/gauges/abstract.py (100%) rename {pyefis => src/pyefis}/instruments/gauges/arc.py (100%) rename {pyefis => src/pyefis}/instruments/gauges/egt.py (100%) rename {pyefis => src/pyefis}/instruments/gauges/horizontalBar.py (100%) rename {pyefis => src/pyefis}/instruments/gauges/numeric.py (100%) rename {pyefis => src/pyefis}/instruments/gauges/verticalBar.py (100%) rename {pyefis => src/pyefis}/instruments/helpers/__init__.py (100%) rename {pyefis => src/pyefis}/instruments/hsi/__init__.py (100%) rename {pyefis => src/pyefis}/instruments/listbox/__init__.py (100%) rename {pyefis => src/pyefis}/instruments/misc/__init__.py (100%) rename {pyefis => src/pyefis}/instruments/pa/__init__.py (100%) rename {pyefis => src/pyefis}/instruments/tc/__init__.py (100%) rename {pyefis => src/pyefis}/instruments/vsi/__init__.py (100%) rename {pyefis => src/pyefis}/instruments/weston/__init__.py (100%) rename {pyefis => src/pyefis}/main.py (100%) rename {pyefis => src/pyefis}/screens/__init__.py (100%) rename {pyefis => src/pyefis}/screens/ems_sm.py (100%) rename {pyefis => src/pyefis}/screens/epfd.py (100%) rename {pyefis => src/pyefis}/screens/pfd.py (100%) rename {pyefis => src/pyefis}/screens/pfd_sm.py (100%) rename {pyefis => src/pyefis}/screens/r582_sm.py (100%) rename {pyefis => src/pyefis}/screens/screenbuilder.py (100%) rename {pyefis => src/pyefis}/screens/sixpack.py (100%) rename {pyefis => src/pyefis}/screens/test.py (100%) rename {pyefis => src/pyefis}/user/__init__.py (100%) rename {pyefis => src/pyefis}/user/hooks/__init__.py (100%) rename {pyefis => src/pyefis}/user/hooks/composite.py (100%) rename {pyefis => src/pyefis}/user/hooks/keys.py (100%) rename {pyefis => src/pyefis}/user/screens/__init__.py (100%) create mode 100644 src/pyefis/version.py diff --git a/AUTHORS b/AUTHORS index e5f18285..8c56ec44 100644 --- a/AUTHORS +++ b/AUTHORS @@ -2,3 +2,4 @@ Phil Birkelbach Neil Domalik Jean-Manuel Gagnon Garrett Herschleb +Eric Blevins diff --git a/Makefile b/Makefile index 4e43d787..46795005 100644 --- a/Makefile +++ b/Makefile @@ -7,9 +7,15 @@ SHELL := /bin/bash ##################################### I N I T T A R G E T S ##################################### venv: python3 -m venv venv + source venv/bin/activate + pip install --upgrade pip + pip install black + pip install pytest + echo "\nRun:\nsource venv/bin/activate" + .PHONY: venv -init.marker: setup.py +init.marker: pyproject.toml pip install -e .[install] touch init.marker init: init.marker @@ -25,3 +31,12 @@ init-build: init-build.marker wheel: init-build python -m build --wheel + + +test: + pip install pytest + pip install pytest-qt + pytest + +.PHONY: test + diff --git a/pyefis/instruments/__init__.py b/pyefis/instruments/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/pyefis/version.py b/pyefis/version.py deleted file mode 100644 index 9027c9c3..00000000 --- a/pyefis/version.py +++ /dev/null @@ -1,3 +0,0 @@ -VERSION="2.0.18" -print(VERSION) - diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..f5ef748f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,63 @@ +[build-system] +requires = [ + "setuptools" +] +build-backend = "setuptools.build_meta" + +#[tool.setuptools] + +[tool.setuptools.dynamic] +version = {attr = "pyefis.__version__"} + +[project] +name = "pyEfis" +dynamic = ["version"] +description = "An Electronic Flight Information System written in Python." +readme = "README.rst" +requires-python = ">=3.8, <4" +authors = [ + { name = "MakerPlane Open Source Aviation", email = "contact@makerplane.org" }, +] +keywords = [ + "aviation", + "efis", + "makerplane", +] +classifiers = [ + "Development Status :: 3 - Alpha", + "Environment :: X11 Applications :: Qt", + "Intended Audience :: End Users/Desktop", + "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.12", +] +dependencies = [ + "geomag==0.9.2015", + "geopy==2.4.1", + "pyavtools==0.1.0", + "pycond==20230212", + "PyQt5==5.15.9", + "PyYAML==6.0.1", +] + +[project.optional-dependencies] +build = [ + "build==1.0.3", +] + +[project.scripts] +pyefis = "pyefis.main:main" + +[project.urls] +Homepage = "https://makerplane.org/" +Source = "https://github.com/makerplane/pyEfis" + + +[tool.pytest.ini_options] +qt_api="pyqt5" + diff --git a/setup.py b/setup.py deleted file mode 100644 index 480df1a6..00000000 --- a/setup.py +++ /dev/null @@ -1,61 +0,0 @@ -"""A setuptools based setup module.""" - -# Always prefer setuptools over distutils -from setuptools import setup, find_packages -import pathlib - -here = pathlib.Path(__file__).parent.resolve() - -# Get the long description from the README file -long_description = (here / "README.rst").read_text(encoding="utf-8") - -setup( - name="pyEfis", - version="0.1.0", - description="An Electronic Flight Information System written in Python.", - long_description=long_description, - long_description_content_type="text/rst", - url="https://github.com/makerplane/pyEfis", - author="MakerPlane Open Source Aviation", - author_email="contact@makerplane.org", - classifiers=[ - "Development Status :: 3 - Alpha", - "Environment :: X11 Applications :: Qt", - "Intended Audience :: End Users/Desktop", - "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3 :: Only", - ], - keywords="aviation, makerplane, efis", - #package_data={ - # "pyefis": ["config/*","config/includes/*", "config/buttons/*"], - #}, - packages=find_packages(exclude=["tests.*", "tests"]), - python_requires=">=3.7, <4", - install_requires=[ - "geomag==0.9.2015", - "PyYAML==6.0.1", - "pyavtools==0.1.0", - "PyQt5==5.15.9", - "pycond==20230212", - "geopy==2.4.1" - ], - extras_require={ - "build": ["build==1.0.3"], - }, - include_package_data= True, - entry_points={ - "console_scripts": [ - "pyefis=pyefis.main:main", - ], - }, - project_urls={ - "Homepage": "https://makerplane.org/", - "Source": "https://github.com/makerplane/pyEfis", - }, -) diff --git a/src/pyefis/__init__.py b/src/pyefis/__init__.py new file mode 100644 index 00000000..d97f221c --- /dev/null +++ b/src/pyefis/__init__.py @@ -0,0 +1,2 @@ +__version__ = "2.0.20" + diff --git a/pyefis/cfg.py b/src/pyefis/cfg.py similarity index 100% rename from pyefis/cfg.py rename to src/pyefis/cfg.py diff --git a/pyefis/common/__init__.py b/src/pyefis/common/__init__.py similarity index 100% rename from pyefis/common/__init__.py rename to src/pyefis/common/__init__.py diff --git a/pyefis/config/buttons/auto-pilot-adjust.yaml b/src/pyefis/config/buttons/auto-pilot-adjust.yaml similarity index 100% rename from pyefis/config/buttons/auto-pilot-adjust.yaml rename to src/pyefis/config/buttons/auto-pilot-adjust.yaml diff --git a/pyefis/config/buttons/auto-pilot-flight-plan.yaml b/src/pyefis/config/buttons/auto-pilot-flight-plan.yaml similarity index 100% rename from pyefis/config/buttons/auto-pilot-flight-plan.yaml rename to src/pyefis/config/buttons/auto-pilot-flight-plan.yaml diff --git a/pyefis/config/buttons/auto-pilot-heading-hold.yaml b/src/pyefis/config/buttons/auto-pilot-heading-hold.yaml similarity index 100% rename from pyefis/config/buttons/auto-pilot-heading-hold.yaml rename to src/pyefis/config/buttons/auto-pilot-heading-hold.yaml diff --git a/pyefis/config/buttons/baro-down-invisible.yaml b/src/pyefis/config/buttons/baro-down-invisible.yaml similarity index 100% rename from pyefis/config/buttons/baro-down-invisible.yaml rename to src/pyefis/config/buttons/baro-down-invisible.yaml diff --git a/pyefis/config/buttons/baro-up-invisible.yaml b/src/pyefis/config/buttons/baro-up-invisible.yaml similarity index 100% rename from pyefis/config/buttons/baro-up-invisible.yaml rename to src/pyefis/config/buttons/baro-up-invisible.yaml diff --git a/pyefis/config/buttons/dimmer-down-invisible.yaml b/src/pyefis/config/buttons/dimmer-down-invisible.yaml similarity index 100% rename from pyefis/config/buttons/dimmer-down-invisible.yaml rename to src/pyefis/config/buttons/dimmer-down-invisible.yaml diff --git a/pyefis/config/buttons/dimmer-up-invisible.yaml b/src/pyefis/config/buttons/dimmer-up-invisible.yaml similarity index 100% rename from pyefis/config/buttons/dimmer-up-invisible.yaml rename to src/pyefis/config/buttons/dimmer-up-invisible.yaml diff --git a/pyefis/config/buttons/egt-Lean.yaml b/src/pyefis/config/buttons/egt-Lean.yaml similarity index 100% rename from pyefis/config/buttons/egt-Lean.yaml rename to src/pyefis/config/buttons/egt-Lean.yaml diff --git a/pyefis/config/buttons/egt-Normalize.yaml b/src/pyefis/config/buttons/egt-Normalize.yaml similarity index 100% rename from pyefis/config/buttons/egt-Normalize.yaml rename to src/pyefis/config/buttons/egt-Normalize.yaml diff --git a/pyefis/config/buttons/egt-Peak.yaml b/src/pyefis/config/buttons/egt-Peak.yaml similarity index 100% rename from pyefis/config/buttons/egt-Peak.yaml rename to src/pyefis/config/buttons/egt-Peak.yaml diff --git a/pyefis/config/buttons/egt-reset-peak.yaml b/src/pyefis/config/buttons/egt-reset-peak.yaml similarity index 100% rename from pyefis/config/buttons/egt-reset-peak.yaml rename to src/pyefis/config/buttons/egt-reset-peak.yaml diff --git a/pyefis/config/buttons/leader.yaml b/src/pyefis/config/buttons/leader.yaml similarity index 100% rename from pyefis/config/buttons/leader.yaml rename to src/pyefis/config/buttons/leader.yaml diff --git a/pyefis/config/buttons/mgl/v16/active-rx-status.yaml b/src/pyefis/config/buttons/mgl/v16/active-rx-status.yaml similarity index 100% rename from pyefis/config/buttons/mgl/v16/active-rx-status.yaml rename to src/pyefis/config/buttons/mgl/v16/active-rx-status.yaml diff --git a/pyefis/config/buttons/mgl/v16/active-tx-status.yaml b/src/pyefis/config/buttons/mgl/v16/active-tx-status.yaml similarity index 100% rename from pyefis/config/buttons/mgl/v16/active-tx-status.yaml rename to src/pyefis/config/buttons/mgl/v16/active-tx-status.yaml diff --git a/pyefis/config/buttons/mgl/v16/standby-rx-status.yaml b/src/pyefis/config/buttons/mgl/v16/standby-rx-status.yaml similarity index 100% rename from pyefis/config/buttons/mgl/v16/standby-rx-status.yaml rename to src/pyefis/config/buttons/mgl/v16/standby-rx-status.yaml diff --git a/pyefis/config/buttons/mgl/v16/swap-active-standby.yaml b/src/pyefis/config/buttons/mgl/v16/swap-active-standby.yaml similarity index 100% rename from pyefis/config/buttons/mgl/v16/swap-active-standby.yaml rename to src/pyefis/config/buttons/mgl/v16/swap-active-standby.yaml diff --git a/pyefis/config/buttons/screen-android-pfd.yaml b/src/pyefis/config/buttons/screen-android-pfd.yaml similarity index 100% rename from pyefis/config/buttons/screen-android-pfd.yaml rename to src/pyefis/config/buttons/screen-android-pfd.yaml diff --git a/pyefis/config/buttons/screen-ems-pfd.yaml b/src/pyefis/config/buttons/screen-ems-pfd.yaml similarity index 100% rename from pyefis/config/buttons/screen-ems-pfd.yaml rename to src/pyefis/config/buttons/screen-ems-pfd.yaml diff --git a/pyefis/config/buttons/screen-ems2-pfd.yaml b/src/pyefis/config/buttons/screen-ems2-pfd.yaml similarity index 100% rename from pyefis/config/buttons/screen-ems2-pfd.yaml rename to src/pyefis/config/buttons/screen-ems2-pfd.yaml diff --git a/pyefis/config/buttons/screen-map-pfd.yaml b/src/pyefis/config/buttons/screen-map-pfd.yaml similarity index 100% rename from pyefis/config/buttons/screen-map-pfd.yaml rename to src/pyefis/config/buttons/screen-map-pfd.yaml diff --git a/pyefis/config/buttons/screen-radio-pfd.yaml b/src/pyefis/config/buttons/screen-radio-pfd.yaml similarity index 100% rename from pyefis/config/buttons/screen-radio-pfd.yaml rename to src/pyefis/config/buttons/screen-radio-pfd.yaml diff --git a/pyefis/config/buttons/screen-sixpack-pfd.yaml b/src/pyefis/config/buttons/screen-sixpack-pfd.yaml similarity index 100% rename from pyefis/config/buttons/screen-sixpack-pfd.yaml rename to src/pyefis/config/buttons/screen-sixpack-pfd.yaml diff --git a/pyefis/config/buttons/trim-center-invisible.yaml b/src/pyefis/config/buttons/trim-center-invisible.yaml similarity index 100% rename from pyefis/config/buttons/trim-center-invisible.yaml rename to src/pyefis/config/buttons/trim-center-invisible.yaml diff --git a/pyefis/config/buttons/trim-down-invisible.yaml b/src/pyefis/config/buttons/trim-down-invisible.yaml similarity index 100% rename from pyefis/config/buttons/trim-down-invisible.yaml rename to src/pyefis/config/buttons/trim-down-invisible.yaml diff --git a/pyefis/config/buttons/trim-roll-center-invisible.yaml b/src/pyefis/config/buttons/trim-roll-center-invisible.yaml similarity index 100% rename from pyefis/config/buttons/trim-roll-center-invisible.yaml rename to src/pyefis/config/buttons/trim-roll-center-invisible.yaml diff --git a/pyefis/config/buttons/trim-roll-left-invisible.yaml b/src/pyefis/config/buttons/trim-roll-left-invisible.yaml similarity index 100% rename from pyefis/config/buttons/trim-roll-left-invisible.yaml rename to src/pyefis/config/buttons/trim-roll-left-invisible.yaml diff --git a/pyefis/config/buttons/trim-roll-right-invisible.yaml b/src/pyefis/config/buttons/trim-roll-right-invisible.yaml similarity index 100% rename from pyefis/config/buttons/trim-roll-right-invisible.yaml rename to src/pyefis/config/buttons/trim-roll-right-invisible.yaml diff --git a/pyefis/config/buttons/trim-up-invisible.yaml b/src/pyefis/config/buttons/trim-up-invisible.yaml similarity index 100% rename from pyefis/config/buttons/trim-up-invisible.yaml rename to src/pyefis/config/buttons/trim-up-invisible.yaml diff --git a/pyefis/config/buttons/trim-yaw-center-invisible.yaml b/src/pyefis/config/buttons/trim-yaw-center-invisible.yaml similarity index 100% rename from pyefis/config/buttons/trim-yaw-center-invisible.yaml rename to src/pyefis/config/buttons/trim-yaw-center-invisible.yaml diff --git a/pyefis/config/buttons/trim-yaw-left-invisible.yaml b/src/pyefis/config/buttons/trim-yaw-left-invisible.yaml similarity index 100% rename from pyefis/config/buttons/trim-yaw-left-invisible.yaml rename to src/pyefis/config/buttons/trim-yaw-left-invisible.yaml diff --git a/pyefis/config/buttons/trim-yaw-right-invisible.yaml b/src/pyefis/config/buttons/trim-yaw-right-invisible.yaml similarity index 100% rename from pyefis/config/buttons/trim-yaw-right-invisible.yaml rename to src/pyefis/config/buttons/trim-yaw-right-invisible.yaml diff --git a/pyefis/config/buttons/units.yaml b/src/pyefis/config/buttons/units.yaml similarity index 100% rename from pyefis/config/buttons/units.yaml rename to src/pyefis/config/buttons/units.yaml diff --git a/pyefis/config/databindings/default.yaml b/src/pyefis/config/databindings/default.yaml similarity index 100% rename from pyefis/config/databindings/default.yaml rename to src/pyefis/config/databindings/default.yaml diff --git a/pyefis/config/default.yaml b/src/pyefis/config/default.yaml similarity index 100% rename from pyefis/config/default.yaml rename to src/pyefis/config/default.yaml diff --git a/pyefis/config/examples/beagle.yaml b/src/pyefis/config/examples/beagle.yaml similarity index 100% rename from pyefis/config/examples/beagle.yaml rename to src/pyefis/config/examples/beagle.yaml diff --git a/pyefis/config/examples/ems.yaml b/src/pyefis/config/examples/ems.yaml similarity index 100% rename from pyefis/config/examples/ems.yaml rename to src/pyefis/config/examples/ems.yaml diff --git a/pyefis/config/examples/rotax_ems.yaml b/src/pyefis/config/examples/rotax_ems.yaml similarity index 100% rename from pyefis/config/examples/rotax_ems.yaml rename to src/pyefis/config/examples/rotax_ems.yaml diff --git a/pyefis/config/hmi/encoder_input.yaml b/src/pyefis/config/hmi/encoder_input.yaml similarity index 100% rename from pyefis/config/hmi/encoder_input.yaml rename to src/pyefis/config/hmi/encoder_input.yaml diff --git a/pyefis/config/includes/ahrs/virtual_vfr.yaml b/src/pyefis/config/includes/ahrs/virtual_vfr.yaml similarity index 100% rename from pyefis/config/includes/ahrs/virtual_vfr.yaml rename to src/pyefis/config/includes/ahrs/virtual_vfr.yaml diff --git a/pyefis/config/includes/arcs/vertical/ds1_TACH1-OILP1-HTOT1-VOLT_ds2_TACH1-OILT1-OAT-CURRNT-segmented.yaml b/src/pyefis/config/includes/arcs/vertical/ds1_TACH1-OILP1-HTOT1-VOLT_ds2_TACH1-OILT1-OAT-CURRNT-segmented.yaml similarity index 100% rename from pyefis/config/includes/arcs/vertical/ds1_TACH1-OILP1-HTOT1-VOLT_ds2_TACH1-OILT1-OAT-CURRNT-segmented.yaml rename to src/pyefis/config/includes/arcs/vertical/ds1_TACH1-OILP1-HTOT1-VOLT_ds2_TACH1-OILT1-OAT-CURRNT-segmented.yaml diff --git a/pyefis/config/includes/arcs/vertical/ds1_TACH1-OILP1-HTOT1-VOLT_ds2_TACH1-OILT1-OAT-CURRNT.yaml b/src/pyefis/config/includes/arcs/vertical/ds1_TACH1-OILP1-HTOT1-VOLT_ds2_TACH1-OILT1-OAT-CURRNT.yaml similarity index 100% rename from pyefis/config/includes/arcs/vertical/ds1_TACH1-OILP1-HTOT1-VOLT_ds2_TACH1-OILT1-OAT-CURRNT.yaml rename to src/pyefis/config/includes/arcs/vertical/ds1_TACH1-OILP1-HTOT1-VOLT_ds2_TACH1-OILT1-OAT-CURRNT.yaml diff --git a/pyefis/config/includes/arcs/vertical/four_high_one_state_preferences_ARC9-12.yaml b/src/pyefis/config/includes/arcs/vertical/four_high_one_state_preferences_ARC9-12.yaml similarity index 100% rename from pyefis/config/includes/arcs/vertical/four_high_one_state_preferences_ARC9-12.yaml rename to src/pyefis/config/includes/arcs/vertical/four_high_one_state_preferences_ARC9-12.yaml diff --git a/pyefis/config/includes/arcs/vertical/four_high_two_states_preferences_ARC1-8.yaml b/src/pyefis/config/includes/arcs/vertical/four_high_two_states_preferences_ARC1-8.yaml similarity index 100% rename from pyefis/config/includes/arcs/vertical/four_high_two_states_preferences_ARC1-8.yaml rename to src/pyefis/config/includes/arcs/vertical/four_high_two_states_preferences_ARC1-8.yaml diff --git a/pyefis/config/includes/arcs/vertical/power_temp_VOLT-CURRNT-OAT-CAT.yaml b/src/pyefis/config/includes/arcs/vertical/power_temp_VOLT-CURRNT-OAT-CAT.yaml similarity index 100% rename from pyefis/config/includes/arcs/vertical/power_temp_VOLT-CURRNT-OAT-CAT.yaml rename to src/pyefis/config/includes/arcs/vertical/power_temp_VOLT-CURRNT-OAT-CAT.yaml diff --git a/pyefis/config/includes/bars/vertical/2_CHT.yaml b/src/pyefis/config/includes/bars/vertical/2_CHT.yaml similarity index 100% rename from pyefis/config/includes/bars/vertical/2_CHT.yaml rename to src/pyefis/config/includes/bars/vertical/2_CHT.yaml diff --git a/pyefis/config/includes/bars/vertical/2_EGT.yaml b/src/pyefis/config/includes/bars/vertical/2_EGT.yaml similarity index 100% rename from pyefis/config/includes/bars/vertical/2_EGT.yaml rename to src/pyefis/config/includes/bars/vertical/2_EGT.yaml diff --git a/pyefis/config/includes/bars/vertical/4_CHT.yaml b/src/pyefis/config/includes/bars/vertical/4_CHT.yaml similarity index 100% rename from pyefis/config/includes/bars/vertical/4_CHT.yaml rename to src/pyefis/config/includes/bars/vertical/4_CHT.yaml diff --git a/pyefis/config/includes/bars/vertical/4_EGT.yaml b/src/pyefis/config/includes/bars/vertical/4_EGT.yaml similarity index 100% rename from pyefis/config/includes/bars/vertical/4_EGT.yaml rename to src/pyefis/config/includes/bars/vertical/4_EGT.yaml diff --git a/pyefis/config/includes/bars/vertical/dimmer_control.yaml b/src/pyefis/config/includes/bars/vertical/dimmer_control.yaml similarity index 100% rename from pyefis/config/includes/bars/vertical/dimmer_control.yaml rename to src/pyefis/config/includes/bars/vertical/dimmer_control.yaml diff --git a/pyefis/config/includes/bars/vertical/fuel_info_FUELP1-FUELF1-FUELQT-FUELQ1-FUELQ2-FUELQ3-segmented.yaml b/src/pyefis/config/includes/bars/vertical/fuel_info_FUELP1-FUELF1-FUELQT-FUELQ1-FUELQ2-FUELQ3-segmented.yaml similarity index 100% rename from pyefis/config/includes/bars/vertical/fuel_info_FUELP1-FUELF1-FUELQT-FUELQ1-FUELQ2-FUELQ3-segmented.yaml rename to src/pyefis/config/includes/bars/vertical/fuel_info_FUELP1-FUELF1-FUELQT-FUELQ1-FUELQ2-FUELQ3-segmented.yaml diff --git a/pyefis/config/includes/bars/vertical/fuel_info_FUELP1-FUELF1-FUELQT-FUELQ1-FUELQ2-FUELQ3.yaml b/src/pyefis/config/includes/bars/vertical/fuel_info_FUELP1-FUELF1-FUELQT-FUELQ1-FUELQ2-FUELQ3.yaml similarity index 100% rename from pyefis/config/includes/bars/vertical/fuel_info_FUELP1-FUELF1-FUELQT-FUELQ1-FUELQ2-FUELQ3.yaml rename to src/pyefis/config/includes/bars/vertical/fuel_info_FUELP1-FUELF1-FUELQT-FUELQ1-FUELQ2-FUELQ3.yaml diff --git a/pyefis/config/includes/bars/vertical/six_wide_preferences_BAR1-6.yaml b/src/pyefis/config/includes/bars/vertical/six_wide_preferences_BAR1-6.yaml similarity index 100% rename from pyefis/config/includes/bars/vertical/six_wide_preferences_BAR1-6.yaml rename to src/pyefis/config/includes/bars/vertical/six_wide_preferences_BAR1-6.yaml diff --git a/pyefis/config/includes/buttons/vertical/screen_changing_PFD-EMS-EMS2-ANDROID-RADIO-SIXPACK-Units.yaml b/src/pyefis/config/includes/buttons/vertical/screen_changing_PFD-EMS-EMS2-ANDROID-RADIO-SIXPACK-Units.yaml similarity index 100% rename from pyefis/config/includes/buttons/vertical/screen_changing_PFD-EMS-EMS2-ANDROID-RADIO-SIXPACK-Units.yaml rename to src/pyefis/config/includes/buttons/vertical/screen_changing_PFD-EMS-EMS2-ANDROID-RADIO-SIXPACK-Units.yaml diff --git a/pyefis/config/includes/buttons/vertical/screen_changing_PFD-EMS-MAP-RADIO-AP-Units.yaml b/src/pyefis/config/includes/buttons/vertical/screen_changing_PFD-EMS-MAP-RADIO-AP-Units.yaml similarity index 100% rename from pyefis/config/includes/buttons/vertical/screen_changing_PFD-EMS-MAP-RADIO-AP-Units.yaml rename to src/pyefis/config/includes/buttons/vertical/screen_changing_PFD-EMS-MAP-RADIO-AP-Units.yaml diff --git a/pyefis/config/includes/mgl/v16/radio-volumes-controls-display.yaml b/src/pyefis/config/includes/mgl/v16/radio-volumes-controls-display.yaml similarity index 100% rename from pyefis/config/includes/mgl/v16/radio-volumes-controls-display.yaml rename to src/pyefis/config/includes/mgl/v16/radio-volumes-controls-display.yaml diff --git a/pyefis/config/includes/mgl/v16/rectangle-radio-volume-primary-secondary-display.yaml b/src/pyefis/config/includes/mgl/v16/rectangle-radio-volume-primary-secondary-display.yaml similarity index 100% rename from pyefis/config/includes/mgl/v16/rectangle-radio-volume-primary-secondary-display.yaml rename to src/pyefis/config/includes/mgl/v16/rectangle-radio-volume-primary-secondary-display.yaml diff --git a/pyefis/config/includes/mgl/v16/square-active-radio-display.yaml b/src/pyefis/config/includes/mgl/v16/square-active-radio-display.yaml similarity index 100% rename from pyefis/config/includes/mgl/v16/square-active-radio-display.yaml rename to src/pyefis/config/includes/mgl/v16/square-active-radio-display.yaml diff --git a/pyefis/config/includes/mgl/v16/square-standby-radio-display.yaml b/src/pyefis/config/includes/mgl/v16/square-standby-radio-display.yaml similarity index 100% rename from pyefis/config/includes/mgl/v16/square-standby-radio-display.yaml rename to src/pyefis/config/includes/mgl/v16/square-standby-radio-display.yaml diff --git a/pyefis/config/includes/trim/pitch-slider-button-control.yaml b/src/pyefis/config/includes/trim/pitch-slider-button-control.yaml similarity index 100% rename from pyefis/config/includes/trim/pitch-slider-button-control.yaml rename to src/pyefis/config/includes/trim/pitch-slider-button-control.yaml diff --git a/pyefis/config/includes/trim/pitch-yaw-roll-combined.yaml b/src/pyefis/config/includes/trim/pitch-yaw-roll-combined.yaml similarity index 100% rename from pyefis/config/includes/trim/pitch-yaw-roll-combined.yaml rename to src/pyefis/config/includes/trim/pitch-yaw-roll-combined.yaml diff --git a/pyefis/config/includes/trim/roll-slider-button-control.yaml b/src/pyefis/config/includes/trim/roll-slider-button-control.yaml similarity index 100% rename from pyefis/config/includes/trim/roll-slider-button-control.yaml rename to src/pyefis/config/includes/trim/roll-slider-button-control.yaml diff --git a/pyefis/config/includes/trim/yaw-slider-button-control.yaml b/src/pyefis/config/includes/trim/yaw-slider-button-control.yaml similarity index 100% rename from pyefis/config/includes/trim/yaw-slider-button-control.yaml rename to src/pyefis/config/includes/trim/yaw-slider-button-control.yaml diff --git a/pyefis/config/keybindings/default.yaml b/src/pyefis/config/keybindings/default.yaml similarity index 100% rename from pyefis/config/keybindings/default.yaml rename to src/pyefis/config/keybindings/default.yaml diff --git a/pyefis/config/lists/radio/favorites.yaml b/src/pyefis/config/lists/radio/favorites.yaml similarity index 100% rename from pyefis/config/lists/radio/favorites.yaml rename to src/pyefis/config/lists/radio/favorites.yaml diff --git a/pyefis/config/lists/radio/listbox.yaml b/src/pyefis/config/lists/radio/listbox.yaml similarity index 100% rename from pyefis/config/lists/radio/listbox.yaml rename to src/pyefis/config/lists/radio/listbox.yaml diff --git a/pyefis/config/lists/radio/ohio.yaml b/src/pyefis/config/lists/radio/ohio.yaml similarity index 100% rename from pyefis/config/lists/radio/ohio.yaml rename to src/pyefis/config/lists/radio/ohio.yaml diff --git a/pyefis/config/logging/file-debug.yaml b/src/pyefis/config/logging/file-debug.yaml similarity index 100% rename from pyefis/config/logging/file-debug.yaml rename to src/pyefis/config/logging/file-debug.yaml diff --git a/pyefis/config/logging/stderr-debug.yaml b/src/pyefis/config/logging/stderr-debug.yaml similarity index 100% rename from pyefis/config/logging/stderr-debug.yaml rename to src/pyefis/config/logging/stderr-debug.yaml diff --git a/pyefis/config/logging/stderr-info.yaml b/src/pyefis/config/logging/stderr-info.yaml similarity index 100% rename from pyefis/config/logging/stderr-info.yaml rename to src/pyefis/config/logging/stderr-info.yaml diff --git a/pyefis/config/main/default.yaml b/src/pyefis/config/main/default.yaml similarity index 100% rename from pyefis/config/main/default.yaml rename to src/pyefis/config/main/default.yaml diff --git a/pyefis/config/outputs/default.yaml b/src/pyefis/config/outputs/default.yaml similarity index 100% rename from pyefis/config/outputs/default.yaml rename to src/pyefis/config/outputs/default.yaml diff --git a/pyefis/config/preferences.yaml b/src/pyefis/config/preferences.yaml similarity index 100% rename from pyefis/config/preferences.yaml rename to src/pyefis/config/preferences.yaml diff --git a/pyefis/config/preferences.yaml.custom b/src/pyefis/config/preferences.yaml.custom similarity index 100% rename from pyefis/config/preferences.yaml.custom rename to src/pyefis/config/preferences.yaml.custom diff --git a/pyefis/config/screens/android-left-buttons.yaml b/src/pyefis/config/screens/android-left-buttons.yaml similarity index 100% rename from pyefis/config/screens/android-left-buttons.yaml rename to src/pyefis/config/screens/android-left-buttons.yaml diff --git a/pyefis/config/screens/android.yaml b/src/pyefis/config/screens/android.yaml similarity index 100% rename from pyefis/config/screens/android.yaml rename to src/pyefis/config/screens/android.yaml diff --git a/pyefis/config/screens/default_list.yaml b/src/pyefis/config/screens/default_list.yaml similarity index 100% rename from pyefis/config/screens/default_list.yaml rename to src/pyefis/config/screens/default_list.yaml diff --git a/pyefis/config/screens/ems-left-buttons.yaml b/src/pyefis/config/screens/ems-left-buttons.yaml similarity index 100% rename from pyefis/config/screens/ems-left-buttons.yaml rename to src/pyefis/config/screens/ems-left-buttons.yaml diff --git a/pyefis/config/screens/ems.yaml b/src/pyefis/config/screens/ems.yaml similarity index 100% rename from pyefis/config/screens/ems.yaml rename to src/pyefis/config/screens/ems.yaml diff --git a/pyefis/config/screens/ems2-left-buttons.yaml b/src/pyefis/config/screens/ems2-left-buttons.yaml similarity index 100% rename from pyefis/config/screens/ems2-left-buttons.yaml rename to src/pyefis/config/screens/ems2-left-buttons.yaml diff --git a/pyefis/config/screens/ems2.yaml b/src/pyefis/config/screens/ems2.yaml similarity index 100% rename from pyefis/config/screens/ems2.yaml rename to src/pyefis/config/screens/ems2.yaml diff --git a/pyefis/config/screens/pfd-left-buttons.yaml b/src/pyefis/config/screens/pfd-left-buttons.yaml similarity index 100% rename from pyefis/config/screens/pfd-left-buttons.yaml rename to src/pyefis/config/screens/pfd-left-buttons.yaml diff --git a/pyefis/config/screens/pfd.yaml b/src/pyefis/config/screens/pfd.yaml similarity index 100% rename from pyefis/config/screens/pfd.yaml rename to src/pyefis/config/screens/pfd.yaml diff --git a/pyefis/config/screens/radio-left-buttons.yaml b/src/pyefis/config/screens/radio-left-buttons.yaml similarity index 100% rename from pyefis/config/screens/radio-left-buttons.yaml rename to src/pyefis/config/screens/radio-left-buttons.yaml diff --git a/pyefis/config/screens/radio.yaml b/src/pyefis/config/screens/radio.yaml similarity index 100% rename from pyefis/config/screens/radio.yaml rename to src/pyefis/config/screens/radio.yaml diff --git a/pyefis/config/screens/sixpack-left-buttons.yaml b/src/pyefis/config/screens/sixpack-left-buttons.yaml similarity index 100% rename from pyefis/config/screens/sixpack-left-buttons.yaml rename to src/pyefis/config/screens/sixpack-left-buttons.yaml diff --git a/pyefis/config/screens/sixpack.yaml b/src/pyefis/config/screens/sixpack.yaml similarity index 100% rename from pyefis/config/screens/sixpack.yaml rename to src/pyefis/config/screens/sixpack.yaml diff --git a/pyefis/config/screens/virtualvfr_db.yaml b/src/pyefis/config/screens/virtualvfr_db.yaml similarity index 100% rename from pyefis/config/screens/virtualvfr_db.yaml rename to src/pyefis/config/screens/virtualvfr_db.yaml diff --git a/pyefis/config/weston.ini b/src/pyefis/config/weston.ini similarity index 100% rename from pyefis/config/weston.ini rename to src/pyefis/config/weston.ini diff --git a/pyefis/gui.py b/src/pyefis/gui.py similarity index 100% rename from pyefis/gui.py rename to src/pyefis/gui.py diff --git a/pyefis/hmi/__init__.py b/src/pyefis/hmi/__init__.py similarity index 100% rename from pyefis/hmi/__init__.py rename to src/pyefis/hmi/__init__.py diff --git a/pyefis/hmi/actionclass.py b/src/pyefis/hmi/actionclass.py similarity index 100% rename from pyefis/hmi/actionclass.py rename to src/pyefis/hmi/actionclass.py diff --git a/pyefis/hmi/data.py b/src/pyefis/hmi/data.py similarity index 100% rename from pyefis/hmi/data.py rename to src/pyefis/hmi/data.py diff --git a/pyefis/hmi/functions.py b/src/pyefis/hmi/functions.py similarity index 100% rename from pyefis/hmi/functions.py rename to src/pyefis/hmi/functions.py diff --git a/pyefis/hmi/keys.py b/src/pyefis/hmi/keys.py similarity index 100% rename from pyefis/hmi/keys.py rename to src/pyefis/hmi/keys.py diff --git a/pyefis/hmi/menu.py b/src/pyefis/hmi/menu.py similarity index 100% rename from pyefis/hmi/menu.py rename to src/pyefis/hmi/menu.py diff --git a/pyefis/hooks.py b/src/pyefis/hooks.py similarity index 100% rename from pyefis/hooks.py rename to src/pyefis/hooks.py diff --git a/pyefis/instruments/NumericalDisplay/__init__.py b/src/pyefis/instruments/NumericalDisplay/__init__.py similarity index 100% rename from pyefis/instruments/NumericalDisplay/__init__.py rename to src/pyefis/instruments/NumericalDisplay/__init__.py diff --git a/pyefis/__init__.py b/src/pyefis/instruments/__init__.py similarity index 100% rename from pyefis/__init__.py rename to src/pyefis/instruments/__init__.py diff --git a/pyefis/instruments/ai/VirtualVfr.py b/src/pyefis/instruments/ai/VirtualVfr.py similarity index 100% rename from pyefis/instruments/ai/VirtualVfr.py rename to src/pyefis/instruments/ai/VirtualVfr.py diff --git a/pyefis/instruments/ai/__init__.py b/src/pyefis/instruments/ai/__init__.py similarity index 100% rename from pyefis/instruments/ai/__init__.py rename to src/pyefis/instruments/ai/__init__.py diff --git a/pyefis/instruments/airspeed/__init__.py b/src/pyefis/instruments/airspeed/__init__.py similarity index 100% rename from pyefis/instruments/airspeed/__init__.py rename to src/pyefis/instruments/airspeed/__init__.py diff --git a/pyefis/instruments/altimeter/__init__.py b/src/pyefis/instruments/altimeter/__init__.py similarity index 100% rename from pyefis/instruments/altimeter/__init__.py rename to src/pyefis/instruments/altimeter/__init__.py diff --git a/pyefis/instruments/button/__init__.py b/src/pyefis/instruments/button/__init__.py similarity index 100% rename from pyefis/instruments/button/__init__.py rename to src/pyefis/instruments/button/__init__.py diff --git a/pyefis/instruments/gauges/__init__.py b/src/pyefis/instruments/gauges/__init__.py similarity index 100% rename from pyefis/instruments/gauges/__init__.py rename to src/pyefis/instruments/gauges/__init__.py diff --git a/pyefis/instruments/gauges/abstract.py b/src/pyefis/instruments/gauges/abstract.py similarity index 100% rename from pyefis/instruments/gauges/abstract.py rename to src/pyefis/instruments/gauges/abstract.py diff --git a/pyefis/instruments/gauges/arc.py b/src/pyefis/instruments/gauges/arc.py similarity index 100% rename from pyefis/instruments/gauges/arc.py rename to src/pyefis/instruments/gauges/arc.py diff --git a/pyefis/instruments/gauges/egt.py b/src/pyefis/instruments/gauges/egt.py similarity index 100% rename from pyefis/instruments/gauges/egt.py rename to src/pyefis/instruments/gauges/egt.py diff --git a/pyefis/instruments/gauges/horizontalBar.py b/src/pyefis/instruments/gauges/horizontalBar.py similarity index 100% rename from pyefis/instruments/gauges/horizontalBar.py rename to src/pyefis/instruments/gauges/horizontalBar.py diff --git a/pyefis/instruments/gauges/numeric.py b/src/pyefis/instruments/gauges/numeric.py similarity index 100% rename from pyefis/instruments/gauges/numeric.py rename to src/pyefis/instruments/gauges/numeric.py diff --git a/pyefis/instruments/gauges/verticalBar.py b/src/pyefis/instruments/gauges/verticalBar.py similarity index 100% rename from pyefis/instruments/gauges/verticalBar.py rename to src/pyefis/instruments/gauges/verticalBar.py diff --git a/pyefis/instruments/helpers/__init__.py b/src/pyefis/instruments/helpers/__init__.py similarity index 100% rename from pyefis/instruments/helpers/__init__.py rename to src/pyefis/instruments/helpers/__init__.py diff --git a/pyefis/instruments/hsi/__init__.py b/src/pyefis/instruments/hsi/__init__.py similarity index 100% rename from pyefis/instruments/hsi/__init__.py rename to src/pyefis/instruments/hsi/__init__.py diff --git a/pyefis/instruments/listbox/__init__.py b/src/pyefis/instruments/listbox/__init__.py similarity index 100% rename from pyefis/instruments/listbox/__init__.py rename to src/pyefis/instruments/listbox/__init__.py diff --git a/pyefis/instruments/misc/__init__.py b/src/pyefis/instruments/misc/__init__.py similarity index 100% rename from pyefis/instruments/misc/__init__.py rename to src/pyefis/instruments/misc/__init__.py diff --git a/pyefis/instruments/pa/__init__.py b/src/pyefis/instruments/pa/__init__.py similarity index 100% rename from pyefis/instruments/pa/__init__.py rename to src/pyefis/instruments/pa/__init__.py diff --git a/pyefis/instruments/tc/__init__.py b/src/pyefis/instruments/tc/__init__.py similarity index 100% rename from pyefis/instruments/tc/__init__.py rename to src/pyefis/instruments/tc/__init__.py diff --git a/pyefis/instruments/vsi/__init__.py b/src/pyefis/instruments/vsi/__init__.py similarity index 100% rename from pyefis/instruments/vsi/__init__.py rename to src/pyefis/instruments/vsi/__init__.py diff --git a/pyefis/instruments/weston/__init__.py b/src/pyefis/instruments/weston/__init__.py similarity index 100% rename from pyefis/instruments/weston/__init__.py rename to src/pyefis/instruments/weston/__init__.py diff --git a/pyefis/main.py b/src/pyefis/main.py similarity index 100% rename from pyefis/main.py rename to src/pyefis/main.py diff --git a/pyefis/screens/__init__.py b/src/pyefis/screens/__init__.py similarity index 100% rename from pyefis/screens/__init__.py rename to src/pyefis/screens/__init__.py diff --git a/pyefis/screens/ems_sm.py b/src/pyefis/screens/ems_sm.py similarity index 100% rename from pyefis/screens/ems_sm.py rename to src/pyefis/screens/ems_sm.py diff --git a/pyefis/screens/epfd.py b/src/pyefis/screens/epfd.py similarity index 100% rename from pyefis/screens/epfd.py rename to src/pyefis/screens/epfd.py diff --git a/pyefis/screens/pfd.py b/src/pyefis/screens/pfd.py similarity index 100% rename from pyefis/screens/pfd.py rename to src/pyefis/screens/pfd.py diff --git a/pyefis/screens/pfd_sm.py b/src/pyefis/screens/pfd_sm.py similarity index 100% rename from pyefis/screens/pfd_sm.py rename to src/pyefis/screens/pfd_sm.py diff --git a/pyefis/screens/r582_sm.py b/src/pyefis/screens/r582_sm.py similarity index 100% rename from pyefis/screens/r582_sm.py rename to src/pyefis/screens/r582_sm.py diff --git a/pyefis/screens/screenbuilder.py b/src/pyefis/screens/screenbuilder.py similarity index 100% rename from pyefis/screens/screenbuilder.py rename to src/pyefis/screens/screenbuilder.py diff --git a/pyefis/screens/sixpack.py b/src/pyefis/screens/sixpack.py similarity index 100% rename from pyefis/screens/sixpack.py rename to src/pyefis/screens/sixpack.py diff --git a/pyefis/screens/test.py b/src/pyefis/screens/test.py similarity index 100% rename from pyefis/screens/test.py rename to src/pyefis/screens/test.py diff --git a/pyefis/user/__init__.py b/src/pyefis/user/__init__.py similarity index 100% rename from pyefis/user/__init__.py rename to src/pyefis/user/__init__.py diff --git a/pyefis/user/hooks/__init__.py b/src/pyefis/user/hooks/__init__.py similarity index 100% rename from pyefis/user/hooks/__init__.py rename to src/pyefis/user/hooks/__init__.py diff --git a/pyefis/user/hooks/composite.py b/src/pyefis/user/hooks/composite.py similarity index 100% rename from pyefis/user/hooks/composite.py rename to src/pyefis/user/hooks/composite.py diff --git a/pyefis/user/hooks/keys.py b/src/pyefis/user/hooks/keys.py similarity index 100% rename from pyefis/user/hooks/keys.py rename to src/pyefis/user/hooks/keys.py diff --git a/pyefis/user/screens/__init__.py b/src/pyefis/user/screens/__init__.py similarity index 100% rename from pyefis/user/screens/__init__.py rename to src/pyefis/user/screens/__init__.py diff --git a/src/pyefis/version.py b/src/pyefis/version.py new file mode 100644 index 00000000..cadc6a8e --- /dev/null +++ b/src/pyefis/version.py @@ -0,0 +1,3 @@ +import pyefis +print(pyefis.__version__) + From f07160788af59658f15657b5f3898fa43a59de84 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Fri, 7 Jun 2024 18:03:35 -0400 Subject: [PATCH 02/60] Change pyavtools dep to use latest github revision since PyPy is outdated --- pyproject.toml | 3 ++- snap/snapcraft.yaml | 4 +--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f5ef748f..3f4d030e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,8 @@ classifiers = [ dependencies = [ "geomag==0.9.2015", "geopy==2.4.1", - "pyavtools==0.1.0", +# Until this revision is published to PyPy get it from git: + "pyavtools@git+https://github.com/makerplane/pyAvTools/#egg=@b00273a9016e9a4b975f8efe51d63405103d58fb", "pycond==20230212", "PyQt5==5.15.9", "PyYAML==6.0.1", diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 508e2b1f..0fae6c6f 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -91,10 +91,8 @@ parts: source: . # Use the OS provided pyqt5 to reduce build issues override-build: | - sed -i 's/"PyQt5==5.15.9",//' setup.py - sed -i 's/pyavtools==0.1.0/pyavtools @ git+https:\/\/github.com\/makerplane\/pyAvTools\/@b00273a9016e9a4b975f8efe51d63405103d58fb#egg=pyavtools/' setup.py craftctl default - craftctl set version=$(python3 pyefis/version.py) + craftctl set version=$(python3 src/pyefis/version.py) stage-packages: - libqt5core5t64 - libqt5qml5 From 5b3588973b77fe4165526493d6cb1beb2851294c Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Fri, 7 Jun 2024 18:51:52 -0400 Subject: [PATCH 03/60] Fixed version and snap build --- pyproject.toml | 2 +- snap/snapcraft.yaml | 1 + src/pyefis/main.py | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3f4d030e..144e3b86 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,7 @@ dependencies = [ # Until this revision is published to PyPy get it from git: "pyavtools@git+https://github.com/makerplane/pyAvTools/#egg=@b00273a9016e9a4b975f8efe51d63405103d58fb", "pycond==20230212", - "PyQt5==5.15.9", + "PyQt5==5.15.10", "PyYAML==6.0.1", ] diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 0fae6c6f..7e40c266 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -91,6 +91,7 @@ parts: source: . # Use the OS provided pyqt5 to reduce build issues override-build: | + sed -i 's/"PyQt5==5.15.10",//' pyproject.toml craftctl default craftctl set version=$(python3 src/pyefis/version.py) stage-packages: diff --git a/src/pyefis/main.py b/src/pyefis/main.py index 28e38d6d..2a594841 100755 --- a/src/pyefis/main.py +++ b/src/pyefis/main.py @@ -16,7 +16,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. import time -from pyefis import version import sys, os import logging @@ -32,6 +31,7 @@ import hashlib from pyefis import cfg +from pyefis import __version__ if "pyAvTools" not in ''.join(sys.path): neighbor_tools = os.path.join('..', 'pyAvTools') @@ -224,7 +224,7 @@ def main(): log.critical("fix database not fully Initialized yet, ensure you have 'ZZLOADER' created in fixgateway database.yaml") time.sleep(2) pyefis_ver = fix.db.get_item('PYEFIS_VERSION') - pyefis_ver.value = version.VERSION + pyefis_ver.value = __version__ pyefis_ver.output_value() hmi.initialize(config) From 3ff1b44de29d8a5a1780ebd12202f0177b62d719 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Fri, 7 Jun 2024 20:35:36 -0400 Subject: [PATCH 04/60] Added first unit test, still need a little work --- pyproject.toml | 5 +++- src/pyefis/instruments/misc/__init__.py | 40 +++++++++++++++---------- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 144e3b86..38737f74 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,4 +61,7 @@ Source = "https://github.com/makerplane/pyEfis" [tool.pytest.ini_options] qt_api="pyqt5" - +filterwarnings = [ + # I belive this is warning that pyqt needs updated if you update to python 3.12 + "ignore:sipPyTypeDict.*:DeprecationWarning:.*:" +] diff --git a/src/pyefis/instruments/misc/__init__.py b/src/pyefis/instruments/misc/__init__.py index 85758078..5e28c462 100644 --- a/src/pyefis/instruments/misc/__init__.py +++ b/src/pyefis/instruments/misc/__init__.py @@ -21,11 +21,20 @@ import pyavtools.fix as fix from pyefis.instruments import helpers + class StaticText(QWidget): """Represents a simple static text display. This is very simple and is - really just here to keep the individual screens from having to have - a painter object and a redraw event handler""" - def __init__(self, text="", fontsize=1.0, color=QColor(Qt.white), parent=None, font_family="DejaVu Sans Condensed"): + really just here to keep the individual screens from having to have + a painter object and a redraw event handler""" + + def __init__( + self, + text="", + fontsize=1.0, + color=QColor(Qt.white), + parent=None, + font_family="DejaVu Sans Condensed", + ): super(StaticText, self).__init__(parent) self.font_family = font_family self.font_ghost_mask = None @@ -39,10 +48,12 @@ def __init__(self, text="", fontsize=1.0, color=QColor(Qt.white), parent=None, f def resizeEvent(self, event): self.Font = QFont(self.font_family) if self.font_mask: - self.font_size = helpers.fit_to_mask(self.width(),self.height(),self.font_mask,self.font_family) + self.font_size = helpers.fit_to_mask( + self.width(), self.height(), self.font_mask, self.font_family + ) self.Font.setPointSizeF(self.font_size) else: - self.Font.setPixelSize(qRound(self.height()*self.font_percent)) + self.Font.setPixelSize(qRound(self.height() * self.font_percent)) self.textRect = QRectF(0, 0, self.width(), self.height()) def paintEvent(self, event): @@ -65,7 +76,7 @@ def paintEvent(self, event): p.setPen(pen) p.drawText(self.textRect, self.font_ghost_mask, opt) self.color.setAlpha(alpha) - + pen.setColor(self.color) p.setPen(pen) p.drawText(self.textRect, self.text, opt) @@ -108,14 +119,17 @@ def __init__(self, parent=None, font_family="Open Sans"): # These are the colors that are actually used # for drawing gauge. self.bgColor = self.bgGoodColor - self.textColor = self.textGoodColor # Non value text like units + self.textColor = self.textGoodColor # Non value text like units self.highlightColor = self.highlightGoodColor self.font_mask = None + def resizeEvent(self, event): self.font = QFont(self.font_family) if self.font_mask: - self.font_size = helpers.fit_to_mask(self.width(),self.height(),self.font_mask,self.font_family) + self.font_size = helpers.fit_to_mask( + self.width(), self.height(), self.font_mask, self.font_family + ) self.font.setPointSizeF(self.font_size) else: self.font.setPixelSize(qRound(self.height() * self.font_percent)) @@ -141,13 +155,11 @@ def paintEvent(self, event): p.setPen(pen) p.drawText(self.valueRect, self.font_ghost_mask, opt) self.textColor.setAlpha(alpha) - + pen.setColor(self.textColor) p.setPen(pen) p.drawText(self.valueRect, self.valueText, opt) - - def getValue(self): return self._value @@ -165,7 +177,7 @@ def setValue(self, value): def getValueText(self): if self.fail: - return 'xxx' + return "xxx" else: return str(self.value) @@ -187,7 +199,6 @@ def setDbkey(self, key): dbkey = property(getDbkey, setDbkey) - # This should get called when the gauge is created and then again # anytime a new report of the db item is recieved from the server def setupGauge(self): @@ -207,11 +218,10 @@ def setupGauge(self): self.item.valueChanged[bool].disconnect(self.setValue) self.item.valueChanged[str].disconnect(self.setValue) except: - pass # One will probably fail all the time + pass # One will probably fail all the time self.item.valueChanged[self.item.dtype].connect(self.setValue) - def setColors(self): if self.bad or self.fail or self.old: self.bgColor = self.bgBadColor From f5d131449dbb7c4761b47c646f519dc00ebc6f93 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Fri, 7 Jun 2024 23:25:04 -0400 Subject: [PATCH 05/60] Updated Makefile with new options and fixes, added test that takes screenshots --- Makefile | 38 +++++---- extras/extras/test_results/.save | 0 pyproject.toml | 5 +- tests/__init__.py | 0 tests/instruments/__init__.py | 0 tests/instruments/misc/test_misc.py | 119 ++++++++++++++++++++++++++++ 6 files changed, 146 insertions(+), 16 deletions(-) create mode 100644 extras/extras/test_results/.save create mode 100644 tests/__init__.py create mode 100644 tests/instruments/__init__.py create mode 100644 tests/instruments/misc/test_misc.py diff --git a/Makefile b/Makefile index 46795005..8ea320fc 100644 --- a/Makefile +++ b/Makefile @@ -5,38 +5,46 @@ SHELL := /bin/bash ##################################### I N I T T A R G E T S ##################################### -venv: +venv.marker: python3 -m venv venv - source venv/bin/activate - pip install --upgrade pip - pip install black - pip install pytest - echo "\nRun:\nsource venv/bin/activate" - + source venv/bin/activate ; pip install --upgrade pip + source venv/bin/activate ; pip install black + source venv/bin/activate ; pip install pytest + source venv/bin/activate ; pip install pytest-qt + source venv/bin/activate ; pip install pytest-env + touch venv.marker + echo -e "\nRun:\nsource venv/bin/activate" +venv: venv.marker .PHONY: venv init.marker: pyproject.toml - pip install -e .[install] + source venv/bin/activate ; pip install -e .[install] touch init.marker -init: init.marker +init: venv.marker init.marker .PHONY: init #################################### W H E E L T A R G E T S #################################### init-build.marker: init - pip install -e .[build] + source venv/bin/activate ; pip install -e .[build] touch init-build.marker init-build: init-build.marker .PHONY: init-build wheel: init-build - python -m build --wheel + source venv/bin/activate ; python -m build --wheel -test: - pip install pytest - pip install pytest-qt - pytest +test: init + source venv/bin/activate ; pytest .PHONY: test +clean: + rm -rf venv + rm -f extras/extras/test_results/*.html + rm -f extras/extras/test_results/*.png + rm -f init-build.marker + rm -f init.marker + rm -f venv.marker +.PHONY: clean diff --git a/extras/extras/test_results/.save b/extras/extras/test_results/.save new file mode 100644 index 00000000..e69de29b diff --git a/pyproject.toml b/pyproject.toml index 38737f74..c40bb409 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,7 +40,7 @@ dependencies = [ "geomag==0.9.2015", "geopy==2.4.1", # Until this revision is published to PyPy get it from git: - "pyavtools@git+https://github.com/makerplane/pyAvTools/#egg=@b00273a9016e9a4b975f8efe51d63405103d58fb", + "pyavtools @ git+https://github.com/makerplane/pyAvTools/@b00273a9016e9a4b975f8efe51d63405103d58fb", "pycond==20230212", "PyQt5==5.15.10", "PyYAML==6.0.1", @@ -61,6 +61,9 @@ Source = "https://github.com/makerplane/pyEfis" [tool.pytest.ini_options] qt_api="pyqt5" +env = [ + "QT_QPA_PLATFORM = offscreen" +] filterwarnings = [ # I belive this is warning that pyqt needs updated if you update to python 3.12 "ignore:sipPyTypeDict.*:DeprecationWarning:.*:" diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/instruments/__init__.py b/tests/instruments/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/instruments/misc/test_misc.py b/tests/instruments/misc/test_misc.py new file mode 100644 index 00000000..603d1d71 --- /dev/null +++ b/tests/instruments/misc/test_misc.py @@ -0,0 +1,119 @@ +import pytest +import warnings +from unittest import mock +from PyQt5.QtWidgets import QApplication +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QColor + +from pyefis.instruments.misc import StaticText, ValueDisplay +import time +import subprocess +import os + + +@pytest.fixture +def app(qtbot): + test_app = QApplication.instance() + if test_app is None: + test_app = QApplication([]) + return test_app + +def test_save_screenshot(qtbot,request): + widget1 = StaticText(text="Testing!") + widget1.font_mask = "XXXXXXXX" + widget1.color = QColor(Qt.black) + #qtbot.addWidget(widget1) + widget1.move(0,0) + widget1.resize(100, 50) + #qtbot.waitExposed(widget1) + #assert widget1.width() == 100 + #assert widget1.height() == 10 + qtbot.addWidget(widget1) + widget1.show() + qtbot.waitExposed(widget1) + path = qtbot.screenshot(widget1,"instruments-misc-test_misc") + qtbot.wait(500) + os.rename(path, request.config.rootdir + "/extras/extras/test_results/instruments-misc-test_misc-StaticText.png") + + +def test_static_text_default(qtbot): + widget = StaticText(text="Testing!") + qtbot.addWidget(widget) + + assert widget.text == "Testing!" + assert widget.font_family == "DejaVu Sans Condensed" + assert widget.color == QColor(Qt.white) + + widget.show() + assert widget.isVisible() + #qtbot.waitForWindowShown(widget) + #time.sleep(3) + +def test_static_text_resize(qtbot): + widget = StaticText(text="Test", fontsize=1.0) + qtbot.addWidget(widget) + + widget.resize(200, 100) + qtbot.waitExposed(widget) + assert widget.width() == 200 + assert widget.height() == 100 + +def test_value_display_default(qtbot): + widget = ValueDisplay() + qtbot.addWidget(widget) + + assert widget.font_family == "Open Sans" + assert widget._value == 0.0 + assert widget.fail == False + assert widget.bad == False + assert widget.old == False + assert widget.annunciate == False + + widget.show() + assert widget.isVisible() + +def test_value_display_set_value(qtbot): + widget = ValueDisplay() + qtbot.addWidget(widget) + + widget.setValue(42.0) + assert widget.getValue() == 42.0 + + widget.setValue(-3.14) + assert widget.getValue() == -3.14 + +@mock.patch('pyefis.instruments.misc.fix') +def test_value_display_flags(mock_fix, qtbot): + mock_item = mock.Mock() + mock_item.value = 100.0 + mock_fix.db.get_item.return_value = mock_item + + widget = ValueDisplay() + qtbot.addWidget(widget) + + widget.failFlag(True) + assert widget.fail == True + assert widget.getValue() == 0.0 + + widget.failFlag(False) + assert widget.fail == False + assert widget.getValue() == 100.0 + + widget.oldFlag(True) + assert widget.old == True + assert widget.getValueText() == '100.0' + + widget.badFlag(True) + assert widget.bad == True + assert widget.getValueText() == '100.0' + + widget.failFlag(True) + assert widget.fail == True + assert widget.getValueText() == "xxx" + + widget.annunciateFlag(True) + assert widget.annunciate == True + +if __name__ == "__main__": + pytest.main() + From 97977ef3a8b9f192d7ee4e918310bf86b0ee480e Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Sat, 8 Jun 2024 08:36:26 -0400 Subject: [PATCH 06/60] Added coverage report --- Makefile | 2 ++ pyproject.toml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Makefile b/Makefile index 8ea320fc..6c8ad3a4 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ venv.marker: source venv/bin/activate ; pip install pytest source venv/bin/activate ; pip install pytest-qt source venv/bin/activate ; pip install pytest-env + source venv/bin/activate ; pip install pytest-cov touch venv.marker echo -e "\nRun:\nsource venv/bin/activate" venv: venv.marker @@ -44,6 +45,7 @@ clean: rm -rf venv rm -f extras/extras/test_results/*.html rm -f extras/extras/test_results/*.png + rm -rf extras/extras/test_results/htmlcov/ rm -f init-build.marker rm -f init.marker rm -f venv.marker diff --git a/pyproject.toml b/pyproject.toml index c40bb409..e3953eec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,3 +68,5 @@ filterwarnings = [ # I belive this is warning that pyqt needs updated if you update to python 3.12 "ignore:sipPyTypeDict.*:DeprecationWarning:.*:" ] +addopts = "--cov=pyefis --cov-report html:extras/extras/test_results/htmlcov/ --cov-report term-missing" + From 0882630d75eab1bc809bb46af13b89b415c7cea9 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Sat, 8 Jun 2024 09:49:05 -0400 Subject: [PATCH 07/60] Updated build dependency version to latest --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e3953eec..9ec805b6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,7 @@ dependencies = [ [project.optional-dependencies] build = [ - "build==1.0.3", + "build==1.2.1", ] [project.scripts] From e60f483fb93d524553a4bf82cce917cb0ab3b2a3 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Sat, 8 Jun 2024 11:43:57 -0400 Subject: [PATCH 08/60] Updated readme with new Makefile commands --- README.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.rst b/README.rst index a16e86c3..d6da5386 100644 --- a/README.rst +++ b/README.rst @@ -79,6 +79,15 @@ This creates an index.bin file in CIFP directory Update the config file [Screen.PFD] section dbpath and indexpath with the path names of the FAACIFP18 and index.bin files respectively. +Testing +------------ +To run all of the automated tests and code covreage. + +:: + + $ make test + + Distribution ------------ @@ -114,3 +123,13 @@ All CLI options work as defined. --log-config LOG_CONFIG Alternate logger configuration file + +Cleanup +------------ + +To cleanup all of the test files, virtual environemnt and other changes made by the makefile. This is a destructive command, you may want to review what it does before running it. + +:: + + $ make clean + From 75789145e0bac4a4cf3ac412f2bc1b30d252d743 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Sat, 8 Jun 2024 11:44:45 -0400 Subject: [PATCH 09/60] Require user confirmation in make clean when deleting files in bulk --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 6c8ad3a4..04489100 100644 --- a/Makefile +++ b/Makefile @@ -42,10 +42,10 @@ test: init .PHONY: test clean: - rm -rf venv - rm -f extras/extras/test_results/*.html - rm -f extras/extras/test_results/*.png - rm -rf extras/extras/test_results/htmlcov/ + rm -rfI venv + rm -fI extras/extras/test_results/*.html + rm -fI extras/extras/test_results/*.png + rm -rfI extras/extras/test_results/htmlcov/ rm -f init-build.marker rm -f init.marker rm -f venv.marker From 8f2fb76cf943ebd0082b49e915684cf54d66decb Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Sat, 8 Jun 2024 12:42:21 -0400 Subject: [PATCH 10/60] Added necessary setting for CI coverage comment --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 9ec805b6..8a51c418 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,3 +70,6 @@ filterwarnings = [ ] addopts = "--cov=pyefis --cov-report html:extras/extras/test_results/htmlcov/ --cov-report term-missing" +[tool.coverage.run] +relative_files = true + From 4dd93ef949079b02685601c0b915a16e8341274d Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Sat, 8 Jun 2024 13:18:53 -0400 Subject: [PATCH 11/60] Added options needed for github action comment to work --- pyproject.toml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8a51c418..bbedea58 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,8 +68,13 @@ filterwarnings = [ # I belive this is warning that pyqt needs updated if you update to python 3.12 "ignore:sipPyTypeDict.*:DeprecationWarning:.*:" ] -addopts = "--cov=pyefis --cov-report html:extras/extras/test_results/htmlcov/ --cov-report term-missing" +addopts = "--cov=pyefis --cov-report html:extras/extras/test_results/htmlcov/ --cov-report term-missing --strict-markers" [tool.coverage.run] relative_files = true +[tool.coverage.paths] +source = [ + "src/" +] + From deddb596e0327818c32ea929555f3eb56538f003 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Sat, 8 Jun 2024 13:36:08 -0400 Subject: [PATCH 12/60] Still trying to fix relative_files --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index bbedea58..59172241 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,7 +68,7 @@ filterwarnings = [ # I belive this is warning that pyqt needs updated if you update to python 3.12 "ignore:sipPyTypeDict.*:DeprecationWarning:.*:" ] -addopts = "--cov=pyefis --cov-report html:extras/extras/test_results/htmlcov/ --cov-report term-missing --strict-markers" +addopts = "-vv --cov=pyefis --cov-report html:extras/extras/test_results/htmlcov/ --cov-report term-missing --strict-markers -rfE" [tool.coverage.run] relative_files = true From 18d1eb775fb83fe52702705c929e39dcafbc1ee3 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Sat, 8 Jun 2024 13:54:35 -0400 Subject: [PATCH 13/60] See if chaning --cov=src resolves the issue --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 59172241..00c94d82 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,7 +68,7 @@ filterwarnings = [ # I belive this is warning that pyqt needs updated if you update to python 3.12 "ignore:sipPyTypeDict.*:DeprecationWarning:.*:" ] -addopts = "-vv --cov=pyefis --cov-report html:extras/extras/test_results/htmlcov/ --cov-report term-missing --strict-markers -rfE" +addopts = "-vv --cov=src --cov-report html:extras/extras/test_results/htmlcov/ --cov-report term-missing --strict-markers -rfE" [tool.coverage.run] relative_files = true From 938afa77fbe88d88fe9190b7fd006c3b78321c26 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Sat, 8 Jun 2024 18:18:57 -0400 Subject: [PATCH 14/60] Added unit test for pyefis.common, formatted with black --- src/pyefis/common/__init__.py | 8 ++++---- tests/common/test_init.py | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 tests/common/test_init.py diff --git a/src/pyefis/common/__init__.py b/src/pyefis/common/__init__.py index fc436d0e..cfa936ca 100644 --- a/src/pyefis/common/__init__.py +++ b/src/pyefis/common/__init__.py @@ -17,10 +17,10 @@ # Constant definitions -UP = 'up' -DOWN = 'down' -LEFT = 'left' -RIGHT = 'right' +UP = "up" +DOWN = "down" +LEFT = "left" +RIGHT = "right" def bounds(min, max, value): diff --git a/tests/common/test_init.py b/tests/common/test_init.py new file mode 100644 index 00000000..6374f382 --- /dev/null +++ b/tests/common/test_init.py @@ -0,0 +1,21 @@ +import pytest +from pyefis.common import bounds + +def test_bounds_below_min(): + assert bounds(0, 10, -5) == 0 + +def test_bounds_above_max(): + assert bounds(0, 10, 15) == 10 + +def test_bounds_within_range(): + assert bounds(0, 10, 5) == 5 + +def test_bounds_on_min(): + assert bounds(0, 10, 0) == 0 + +def test_bounds_on_max(): + assert bounds(0, 10, 10) == 10 + +if __name__ == "__main__": + pytest.main() + From 29ad093c1ddf263db199f6e388a480121e677af0 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Sat, 8 Jun 2024 21:34:16 -0400 Subject: [PATCH 15/60] Added unit tests for hmi.actionclass and formatted actionclass with black --- src/pyefis/hmi/actionclass.py | 37 +++++++++++------------ tests/hmi/test_actionclass.py | 57 +++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 19 deletions(-) create mode 100644 tests/hmi/test_actionclass.py diff --git a/src/pyefis/hmi/actionclass.py b/src/pyefis/hmi/actionclass.py index 84bfff06..9d21d971 100644 --- a/src/pyefis/hmi/actionclass.py +++ b/src/pyefis/hmi/actionclass.py @@ -37,32 +37,31 @@ class ActionClass(QWidget): doExit = pyqtSignal(object) # arg = ,,,..: - def __init__(self): super(ActionClass, self).__init__() - self.signalMap = {"set airspeed mode":self.setAirspeedMode, - "set egt mode":self.setEgtMode, - "show screen":self.showScreen, - "show next screen":self.showNextScreen, - "show previous screen":self.showPrevScreen, - "set value":functions.setValue, - "change value":functions.changeValue, - "toggle bit":functions.toggleBool, - "activate menu item":self.activateMenuItem, - "activate menu":activateMenu, - "menu encoder":self.menuEncoder, - "set menu focus":self.setMenuFocus, - "set instrument units":self.setInstUnits, - "exit":self.doExit, - "evaluate":eval - } - + self.signalMap = { + "set airspeed mode": self.setAirspeedMode, + "set egt mode": self.setEgtMode, + "show screen": self.showScreen, + "show next screen": self.showNextScreen, + "show previous screen": self.showPrevScreen, + "set value": functions.setValue, + "change value": functions.changeValue, + "toggle bit": functions.toggleBool, + "activate menu item": self.activateMenuItem, + "activate menu": activateMenu, + "menu encoder": self.menuEncoder, + "set menu focus": self.setMenuFocus, + "set instrument units": self.setInstUnits, + "exit": self.doExit, + "evaluate": eval, + } def trigger(self, action, argument=""): a = self.signalMap[action.lower()] if isinstance(a, pyqtBoundSignal): a.emit(argument) - else: # It's not a signal so assume it's a function + else: # It's not a signal so assume it's a function a(argument) def findAction(self, action): diff --git a/tests/hmi/test_actionclass.py b/tests/hmi/test_actionclass.py new file mode 100644 index 00000000..19b92de1 --- /dev/null +++ b/tests/hmi/test_actionclass.py @@ -0,0 +1,57 @@ +import pytest +from PyQt5.QtCore import pyqtSignal, pyqtBoundSignal +from PyQt5.QtWidgets import QApplication +from unittest import mock #MagicMock, mock.patch +from pyefis.hmi import actionclass #ActionClass + +# Ensure QApplication instance +@pytest.fixture#(scope="module", autouse=True) +def app(qtbot): + test_app = QApplication.instance() + if test_app is None: + test_app = QApplication([]) + return test_app + +def test_initialization(qtbot): + action_class = actionclass.ActionClass() + assert action_class.signalMap is not None + assert "set airspeed mode" in action_class.signalMap + +@mock.patch('pyefis.hmi.functions.setValue') +def test_trigger_function(mock_setValue, qtbot): + action_class = actionclass.ActionClass() + # Mocking the entire functions module + mock_setValue.return_value = None + #mock_db.get_item.return_value = None # Mocking pyavtools.fix.db as well + + action_class.trigger("set value", "test") + + # Asserting that setValue is called + mock_setValue.assert_called_once_with("test") + +def test_trigger_signal(qtbot): + action_class = actionclass.ActionClass() + mock_signal = mock.MagicMock(spec=pyqtBoundSignal) + action_class.signalMap["set airspeed mode"] = mock_signal # mock.patching emit directly + action_class.trigger("set airspeed mode", "test") + mock_signal.emit.assert_called_once_with("test") # Use assert_called_once_with on mock_signal directly + +def test_find_action_exists(qtbot): + action_class = actionclass.ActionClass() + action = action_class.findAction("set airspeed mode") + assert action == action_class.setAirspeedMode + +def test_find_action_not_exists(qtbot): + action_class = actionclass.ActionClass() + action = action_class.findAction("nonexistent action") + assert action is None + +def test_trigger_eval(qtbot): + action_class = actionclass.ActionClass() + mock_eval = mock.MagicMock() + action_class.signalMap["evaluate"] = mock_eval + action_class.trigger("evaluate", "print('test')") + mock_eval.assert_called_once_with("print('test')") + +if __name__ == "__main__": + pytest.main() From d0c62a09cfa300039d9d474ffea014561381c42a Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Sat, 8 Jun 2024 21:38:58 -0400 Subject: [PATCH 16/60] Added test for pyefis.version and formatted with black --- src/pyefis/config/main/default.yaml | 6 +++--- src/pyefis/version.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pyefis/config/main/default.yaml b/src/pyefis/config/main/default.yaml index 05254c70..352f7b58 100644 --- a/src/pyefis/config/main/default.yaml +++ b/src/pyefis/config/main/default.yaml @@ -16,15 +16,15 @@ # Screen Geometry # Defaults to screen size if screenWidth or screenHeight is not defined - #screenWidth: 1280 - #screenHeight: 720 + screenWidth: 1280 + screenHeight: 720 #screenWidth: 1024 #screenHeight: 768 #screenWidth: 1920 #screenHeight: 1080 # Set EFIS to occupy the entire screen without system border / menu - screenFullSize: True + screenFullSize: False # Screen background color RGB screenColor: (0,0,0) diff --git a/src/pyefis/version.py b/src/pyefis/version.py index cadc6a8e..02d15999 100644 --- a/src/pyefis/version.py +++ b/src/pyefis/version.py @@ -1,3 +1,3 @@ import pyefis -print(pyefis.__version__) +print(pyefis.__version__) From d553f8a49cfab403b5106c718d405dd1d6c0ab4c Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Sat, 8 Jun 2024 21:51:35 -0400 Subject: [PATCH 17/60] Added tests for pyefis.hmi.functions and formatted with black --- src/pyefis/hmi/functions.py | 8 +++-- tests/hmi/__init__.py | 0 tests/hmi/test_functions.py | 63 +++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 tests/hmi/__init__.py create mode 100644 tests/hmi/test_functions.py diff --git a/src/pyefis/hmi/functions.py b/src/pyefis/hmi/functions.py index 49e4177f..c05849cc 100644 --- a/src/pyefis/hmi/functions.py +++ b/src/pyefis/hmi/functions.py @@ -16,9 +16,10 @@ import pyavtools.fix as fix + # Set a value in the FIX database. arg should be "key,value" def setValue(arg): - args = arg.split(',') + args = arg.split(",") i = fix.db.get_item(args[0].strip()) i.value = args[1].strip() # Always output values we set @@ -26,15 +27,16 @@ def setValue(arg): # action and most actions take place outside of pyEFIS i.output_value() + # Change a value in the FIX database by a certain value. arg should be "key,value" def changeValue(arg): - args = arg.split(',') + args = arg.split(",") i = fix.db.get_item(args[0]) i.value += i.dtype(args[1]) i.output_value() + def toggleBool(arg): bit = fix.db.get_item(arg) bit.value = not bit.value bit.output_value() - diff --git a/tests/hmi/__init__.py b/tests/hmi/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/hmi/test_functions.py b/tests/hmi/test_functions.py new file mode 100644 index 00000000..445f881e --- /dev/null +++ b/tests/hmi/test_functions.py @@ -0,0 +1,63 @@ +import pytest +from unittest.mock import MagicMock, patch +import pyefis.hmi.functions # Import your module here + +# Mock the fix module +@pytest.fixture +def fix_mock(): + with patch('pyefis.hmi.functions.fix') as fix_mock: + yield fix_mock + +# Test for setValue function +def test_setValue(fix_mock): + # Mocking the arg for setValue + arg = "key,value" + # Setting up the mock for the item + item_mock = MagicMock() + item_mock.value = None + fix_mock.db.get_item.return_value = item_mock + + # Calling the function + pyefis.hmi.functions.setValue(arg) + + # Assertions + fix_mock.db.get_item.assert_called_once_with("key") + assert item_mock.value == "value" + assert item_mock.output_value.called + +# Test for changeValue function +def test_changeValue(fix_mock): + # Mocking the arg for changeValue + arg = "key,1" + # Setting up the mock for the item + item_mock = MagicMock() + item_mock.value = 1 + item_mock.dtype.return_value = 1 + fix_mock.db.get_item.return_value = item_mock + + # Calling the function + pyefis.hmi.functions.changeValue(arg) + + # Assertions + fix_mock.db.get_item.assert_called_once_with("key") + assert item_mock.value == 2 + assert item_mock.dtype.called + assert item_mock.output_value.called + +# Test for toggleBool function +def test_toggleBool(fix_mock): + # Mocking the arg for toggleBool + arg = "key" + # Setting up the mock for the item + item_mock = MagicMock() + item_mock.value = False + fix_mock.db.get_item.return_value = item_mock + + # Calling the function + pyefis.hmi.functions.toggleBool(arg) + + # Assertions + fix_mock.db.get_item.assert_called_once_with("key") + assert item_mock.value == True + assert item_mock.output_value.called + From ced73d6af9d4dbefda4b4a2b4536c042ddbf2eac Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Sat, 8 Jun 2024 22:47:03 -0400 Subject: [PATCH 18/60] saving progress creating tests for pyefis.cfg --- tests/data/cfg/PREFERENCEA.yaml | 1 + tests/data/cfg/a.yaml | 2 ++ tests/data/cfg/array_of_includes.yaml | 4 +++ tests/data/cfg/d.yaml | 2 ++ tests/data/cfg/includea.yaml | 1 + tests/data/cfg/loop1.yaml | 2 ++ tests/data/cfg/loop1a.yaml | 2 ++ tests/data/cfg/not_string.yaml | 1 + tests/data/cfg/sub/b.yaml | 4 +++ tests/data/cfg/sub/c.yaml | 2 ++ tests/hmi/test_functions.py | 2 +- tests/test_cfg.py | 35 +++++++++++++++++++++++++++ 12 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 tests/data/cfg/PREFERENCEA.yaml create mode 100644 tests/data/cfg/a.yaml create mode 100644 tests/data/cfg/array_of_includes.yaml create mode 100644 tests/data/cfg/d.yaml create mode 100644 tests/data/cfg/includea.yaml create mode 100644 tests/data/cfg/loop1.yaml create mode 100644 tests/data/cfg/loop1a.yaml create mode 100644 tests/data/cfg/not_string.yaml create mode 100644 tests/data/cfg/sub/b.yaml create mode 100644 tests/data/cfg/sub/c.yaml create mode 100644 tests/test_cfg.py diff --git a/tests/data/cfg/PREFERENCEA.yaml b/tests/data/cfg/PREFERENCEA.yaml new file mode 100644 index 00000000..d679504c --- /dev/null +++ b/tests/data/cfg/PREFERENCEA.yaml @@ -0,0 +1 @@ +PREFERENCEA: true diff --git a/tests/data/cfg/a.yaml b/tests/data/cfg/a.yaml new file mode 100644 index 00000000..1d93de89 --- /dev/null +++ b/tests/data/cfg/a.yaml @@ -0,0 +1,2 @@ +a: + include: sub/b.yaml diff --git a/tests/data/cfg/array_of_includes.yaml b/tests/data/cfg/array_of_includes.yaml new file mode 100644 index 00000000..79caf116 --- /dev/null +++ b/tests/data/cfg/array_of_includes.yaml @@ -0,0 +1,4 @@ +ARRAYS: + include: + - includea.yaml + - PREFERENCEA diff --git a/tests/data/cfg/d.yaml b/tests/data/cfg/d.yaml new file mode 100644 index 00000000..2156ef05 --- /dev/null +++ b/tests/data/cfg/d.yaml @@ -0,0 +1,2 @@ +d: + end: true diff --git a/tests/data/cfg/includea.yaml b/tests/data/cfg/includea.yaml new file mode 100644 index 00000000..ef7288ac --- /dev/null +++ b/tests/data/cfg/includea.yaml @@ -0,0 +1 @@ +includea: true diff --git a/tests/data/cfg/loop1.yaml b/tests/data/cfg/loop1.yaml new file mode 100644 index 00000000..ea1ce7bb --- /dev/null +++ b/tests/data/cfg/loop1.yaml @@ -0,0 +1,2 @@ +loop1: + include: loop1a.yaml diff --git a/tests/data/cfg/loop1a.yaml b/tests/data/cfg/loop1a.yaml new file mode 100644 index 00000000..206af256 --- /dev/null +++ b/tests/data/cfg/loop1a.yaml @@ -0,0 +1,2 @@ +loop1a: + include: loop1.yaml diff --git a/tests/data/cfg/not_string.yaml b/tests/data/cfg/not_string.yaml new file mode 100644 index 00000000..00671f3e --- /dev/null +++ b/tests/data/cfg/not_string.yaml @@ -0,0 +1 @@ +include: 1 diff --git a/tests/data/cfg/sub/b.yaml b/tests/data/cfg/sub/b.yaml new file mode 100644 index 00000000..da26419f --- /dev/null +++ b/tests/data/cfg/sub/b.yaml @@ -0,0 +1,4 @@ +b: + include: c.yaml +b2: + include: d.yaml diff --git a/tests/data/cfg/sub/c.yaml b/tests/data/cfg/sub/c.yaml new file mode 100644 index 00000000..46296f0b --- /dev/null +++ b/tests/data/cfg/sub/c.yaml @@ -0,0 +1,2 @@ +c: + done: true diff --git a/tests/hmi/test_functions.py b/tests/hmi/test_functions.py index 445f881e..08c75ae3 100644 --- a/tests/hmi/test_functions.py +++ b/tests/hmi/test_functions.py @@ -1,6 +1,6 @@ import pytest from unittest.mock import MagicMock, patch -import pyefis.hmi.functions # Import your module here +import pyefis.hmi.functions # Mock the fix module @pytest.fixture diff --git a/tests/test_cfg.py b/tests/test_cfg.py new file mode 100644 index 00000000..44736765 --- /dev/null +++ b/tests/test_cfg.py @@ -0,0 +1,35 @@ +import pytest +import os +from unittest.mock import MagicMock, patch +from pyefis.cfg import from_yaml + + + +def test_sub_parent_includes(): + d = from_yaml('tests/data/cfg/a.yaml') + assert d == {'a': {'b': {'c': {'done': True}}, 'b2': {'d': {'end': True}}}} + +def test_array_and_preferences(): + e = from_yaml('tests/data/cfg/array_of_includes.yaml', preferences={"includes": {"PREFERENCEA": "PREFERENCEA.yaml"}}) + assert e == {'ARRAYS': {'includea': True, 'PREFERENCEA': True}} + +def test_data_type(): + with pytest.raises(Exception): + d = from_yaml('tests/data/cfg/not_string.yaml') + +def test_loop_detection_exception(): + with pytest.raises(Exception): + d = from_yaml('tests/data/cfg/loop1.yaml') + +# We want to test: +# Various loop exceptions +# +# include: filename +# include: +# - filename1: +# - filename2: +# include: PREFERENCE_NAME +# various uses of 'item' +# include from root or from same sub-folder +# include from another folder + From 3574d0529cbd16f835cd9bc3d75eec8a22b7c34f Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Sun, 9 Jun 2024 14:27:49 -0400 Subject: [PATCH 19/60] Created unit tests for test_cfg.py --- tests/data/cfg/PREFERENCEA.yaml | 1 - tests/data/cfg/a.yaml | 2 - tests/data/cfg/array_of_includes.yaml | 4 -- tests/data/cfg/includea.yaml | 1 - tests/data/cfg/loop1.yaml | 2 - tests/data/cfg/loop1a.yaml | 2 - tests/data/cfg/sub/b.yaml | 4 -- .../sub/sub2/test_list_bpath_preferenceA.yaml | 5 ++ .../sub/sub2/test_list_bpath_preferenceB.yaml | 2 + .../cfg/sub/test_array_and_preferencesB.yaml | 1 + .../cfg/sub/test_array_and_preferencesD.yaml | 1 + .../cfg/sub/test_sub_parent_includesB.yaml | 4 ++ ...{c.yaml => test_sub_parent_includesC.yaml} | 0 .../data/cfg/test_array_and_preferences.yaml | 5 ++ .../data/cfg/test_array_and_preferencesA.yaml | 1 + .../data/cfg/test_array_and_preferencesC.yaml | 1 + .../{not_string.yaml => test_data_type.yaml} | 0 .../data/cfg/test_list_bpath_preference.yaml | 1 + .../data/cfg/test_list_bpath_preferenceC.yaml | 2 + .../cfg/test_list_include_missing_items.yaml | 3 + ...nclude_missing_items_via_preferencesD.yaml | 1 + ...missing_items_via_preferences_nestedF.yaml | 4 ++ ...missing_items_via_preferences_nestedG.yaml | 2 + .../cfg/test_loop_detection_exception.yaml | 2 + .../cfg/test_loop_detection_exceptionA.yaml | 2 + tests/data/cfg/test_no_preferences.yaml | 2 + .../cfg/test_preference_file_not_found.yaml | 1 + tests/data/cfg/test_sub_parent_includesA.yaml | 2 + ...{d.yaml => test_sub_parent_includesD.yaml} | 0 tests/test_cfg.py | 56 ++++++++++++------- tests/test_version.py | 4 ++ 31 files changed, 82 insertions(+), 36 deletions(-) delete mode 100644 tests/data/cfg/PREFERENCEA.yaml delete mode 100644 tests/data/cfg/a.yaml delete mode 100644 tests/data/cfg/array_of_includes.yaml delete mode 100644 tests/data/cfg/includea.yaml delete mode 100644 tests/data/cfg/loop1.yaml delete mode 100644 tests/data/cfg/loop1a.yaml delete mode 100644 tests/data/cfg/sub/b.yaml create mode 100644 tests/data/cfg/sub/sub2/test_list_bpath_preferenceA.yaml create mode 100644 tests/data/cfg/sub/sub2/test_list_bpath_preferenceB.yaml create mode 100644 tests/data/cfg/sub/test_array_and_preferencesB.yaml create mode 100644 tests/data/cfg/sub/test_array_and_preferencesD.yaml create mode 100644 tests/data/cfg/sub/test_sub_parent_includesB.yaml rename tests/data/cfg/sub/{c.yaml => test_sub_parent_includesC.yaml} (100%) create mode 100644 tests/data/cfg/test_array_and_preferences.yaml create mode 100644 tests/data/cfg/test_array_and_preferencesA.yaml create mode 100644 tests/data/cfg/test_array_and_preferencesC.yaml rename tests/data/cfg/{not_string.yaml => test_data_type.yaml} (100%) create mode 100644 tests/data/cfg/test_list_bpath_preference.yaml create mode 100644 tests/data/cfg/test_list_bpath_preferenceC.yaml create mode 100644 tests/data/cfg/test_list_include_missing_items.yaml create mode 100644 tests/data/cfg/test_list_include_missing_items_via_preferencesD.yaml create mode 100644 tests/data/cfg/test_list_include_missing_items_via_preferences_nestedF.yaml create mode 100644 tests/data/cfg/test_list_include_missing_items_via_preferences_nestedG.yaml create mode 100644 tests/data/cfg/test_loop_detection_exception.yaml create mode 100644 tests/data/cfg/test_loop_detection_exceptionA.yaml create mode 100644 tests/data/cfg/test_no_preferences.yaml create mode 100644 tests/data/cfg/test_preference_file_not_found.yaml create mode 100644 tests/data/cfg/test_sub_parent_includesA.yaml rename tests/data/cfg/{d.yaml => test_sub_parent_includesD.yaml} (100%) create mode 100644 tests/test_version.py diff --git a/tests/data/cfg/PREFERENCEA.yaml b/tests/data/cfg/PREFERENCEA.yaml deleted file mode 100644 index d679504c..00000000 --- a/tests/data/cfg/PREFERENCEA.yaml +++ /dev/null @@ -1 +0,0 @@ -PREFERENCEA: true diff --git a/tests/data/cfg/a.yaml b/tests/data/cfg/a.yaml deleted file mode 100644 index 1d93de89..00000000 --- a/tests/data/cfg/a.yaml +++ /dev/null @@ -1,2 +0,0 @@ -a: - include: sub/b.yaml diff --git a/tests/data/cfg/array_of_includes.yaml b/tests/data/cfg/array_of_includes.yaml deleted file mode 100644 index 79caf116..00000000 --- a/tests/data/cfg/array_of_includes.yaml +++ /dev/null @@ -1,4 +0,0 @@ -ARRAYS: - include: - - includea.yaml - - PREFERENCEA diff --git a/tests/data/cfg/includea.yaml b/tests/data/cfg/includea.yaml deleted file mode 100644 index ef7288ac..00000000 --- a/tests/data/cfg/includea.yaml +++ /dev/null @@ -1 +0,0 @@ -includea: true diff --git a/tests/data/cfg/loop1.yaml b/tests/data/cfg/loop1.yaml deleted file mode 100644 index ea1ce7bb..00000000 --- a/tests/data/cfg/loop1.yaml +++ /dev/null @@ -1,2 +0,0 @@ -loop1: - include: loop1a.yaml diff --git a/tests/data/cfg/loop1a.yaml b/tests/data/cfg/loop1a.yaml deleted file mode 100644 index 206af256..00000000 --- a/tests/data/cfg/loop1a.yaml +++ /dev/null @@ -1,2 +0,0 @@ -loop1a: - include: loop1.yaml diff --git a/tests/data/cfg/sub/b.yaml b/tests/data/cfg/sub/b.yaml deleted file mode 100644 index da26419f..00000000 --- a/tests/data/cfg/sub/b.yaml +++ /dev/null @@ -1,4 +0,0 @@ -b: - include: c.yaml -b2: - include: d.yaml diff --git a/tests/data/cfg/sub/sub2/test_list_bpath_preferenceA.yaml b/tests/data/cfg/sub/sub2/test_list_bpath_preferenceA.yaml new file mode 100644 index 00000000..0c8cf43e --- /dev/null +++ b/tests/data/cfg/sub/sub2/test_list_bpath_preferenceA.yaml @@ -0,0 +1,5 @@ +test: + - include: B + - include: C + - 5 + - Ten diff --git a/tests/data/cfg/sub/sub2/test_list_bpath_preferenceB.yaml b/tests/data/cfg/sub/sub2/test_list_bpath_preferenceB.yaml new file mode 100644 index 00000000..b9b87ffb --- /dev/null +++ b/tests/data/cfg/sub/sub2/test_list_bpath_preferenceB.yaml @@ -0,0 +1,2 @@ +items: + b_id: 5 diff --git a/tests/data/cfg/sub/test_array_and_preferencesB.yaml b/tests/data/cfg/sub/test_array_and_preferencesB.yaml new file mode 100644 index 00000000..933ed204 --- /dev/null +++ b/tests/data/cfg/sub/test_array_and_preferencesB.yaml @@ -0,0 +1 @@ +includeB: true diff --git a/tests/data/cfg/sub/test_array_and_preferencesD.yaml b/tests/data/cfg/sub/test_array_and_preferencesD.yaml new file mode 100644 index 00000000..bdcafbc3 --- /dev/null +++ b/tests/data/cfg/sub/test_array_and_preferencesD.yaml @@ -0,0 +1 @@ +include: PREFERENCEB diff --git a/tests/data/cfg/sub/test_sub_parent_includesB.yaml b/tests/data/cfg/sub/test_sub_parent_includesB.yaml new file mode 100644 index 00000000..8827cbe8 --- /dev/null +++ b/tests/data/cfg/sub/test_sub_parent_includesB.yaml @@ -0,0 +1,4 @@ +b: + include: test_sub_parent_includesC.yaml +b2: + include: test_sub_parent_includesD.yaml diff --git a/tests/data/cfg/sub/c.yaml b/tests/data/cfg/sub/test_sub_parent_includesC.yaml similarity index 100% rename from tests/data/cfg/sub/c.yaml rename to tests/data/cfg/sub/test_sub_parent_includesC.yaml diff --git a/tests/data/cfg/test_array_and_preferences.yaml b/tests/data/cfg/test_array_and_preferences.yaml new file mode 100644 index 00000000..145ecd3b --- /dev/null +++ b/tests/data/cfg/test_array_and_preferences.yaml @@ -0,0 +1,5 @@ +ARRAYS: + include: + - test_array_and_preferencesC.yaml + - PREFERENCEA + - sub/test_array_and_preferencesD.yaml diff --git a/tests/data/cfg/test_array_and_preferencesA.yaml b/tests/data/cfg/test_array_and_preferencesA.yaml new file mode 100644 index 00000000..fa77ad86 --- /dev/null +++ b/tests/data/cfg/test_array_and_preferencesA.yaml @@ -0,0 +1 @@ +includeA: true diff --git a/tests/data/cfg/test_array_and_preferencesC.yaml b/tests/data/cfg/test_array_and_preferencesC.yaml new file mode 100644 index 00000000..8bd5f65d --- /dev/null +++ b/tests/data/cfg/test_array_and_preferencesC.yaml @@ -0,0 +1 @@ +includeC: true diff --git a/tests/data/cfg/not_string.yaml b/tests/data/cfg/test_data_type.yaml similarity index 100% rename from tests/data/cfg/not_string.yaml rename to tests/data/cfg/test_data_type.yaml diff --git a/tests/data/cfg/test_list_bpath_preference.yaml b/tests/data/cfg/test_list_bpath_preference.yaml new file mode 100644 index 00000000..a27972d9 --- /dev/null +++ b/tests/data/cfg/test_list_bpath_preference.yaml @@ -0,0 +1 @@ +include: A diff --git a/tests/data/cfg/test_list_bpath_preferenceC.yaml b/tests/data/cfg/test_list_bpath_preferenceC.yaml new file mode 100644 index 00000000..cbad5fbb --- /dev/null +++ b/tests/data/cfg/test_list_bpath_preferenceC.yaml @@ -0,0 +1,2 @@ +items: + - c_id_is: 5 diff --git a/tests/data/cfg/test_list_include_missing_items.yaml b/tests/data/cfg/test_list_include_missing_items.yaml new file mode 100644 index 00000000..d0dd3e26 --- /dev/null +++ b/tests/data/cfg/test_list_include_missing_items.yaml @@ -0,0 +1,3 @@ +list_of_items: + - test: true + - include: test_list_include_missing_itemsA.yaml diff --git a/tests/data/cfg/test_list_include_missing_items_via_preferencesD.yaml b/tests/data/cfg/test_list_include_missing_items_via_preferencesD.yaml new file mode 100644 index 00000000..4477fd43 --- /dev/null +++ b/tests/data/cfg/test_list_include_missing_items_via_preferencesD.yaml @@ -0,0 +1 @@ +no_list_of_items: true diff --git a/tests/data/cfg/test_list_include_missing_items_via_preferences_nestedF.yaml b/tests/data/cfg/test_list_include_missing_items_via_preferences_nestedF.yaml new file mode 100644 index 00000000..6ceb6f9b --- /dev/null +++ b/tests/data/cfg/test_list_include_missing_items_via_preferences_nestedF.yaml @@ -0,0 +1,4 @@ +items: + - test: true + - test2: + - include: PREFERENCEG diff --git a/tests/data/cfg/test_list_include_missing_items_via_preferences_nestedG.yaml b/tests/data/cfg/test_list_include_missing_items_via_preferences_nestedG.yaml new file mode 100644 index 00000000..9d459e8c --- /dev/null +++ b/tests/data/cfg/test_list_include_missing_items_via_preferences_nestedG.yaml @@ -0,0 +1,2 @@ +wrong: + stuff: true diff --git a/tests/data/cfg/test_loop_detection_exception.yaml b/tests/data/cfg/test_loop_detection_exception.yaml new file mode 100644 index 00000000..7f9fab0c --- /dev/null +++ b/tests/data/cfg/test_loop_detection_exception.yaml @@ -0,0 +1,2 @@ +loop1: + include: test_loop_detection_exceptionA.yaml diff --git a/tests/data/cfg/test_loop_detection_exceptionA.yaml b/tests/data/cfg/test_loop_detection_exceptionA.yaml new file mode 100644 index 00000000..0d1c2d1c --- /dev/null +++ b/tests/data/cfg/test_loop_detection_exceptionA.yaml @@ -0,0 +1,2 @@ +loop1a: + include: test_loop_detection_exception.yaml diff --git a/tests/data/cfg/test_no_preferences.yaml b/tests/data/cfg/test_no_preferences.yaml new file mode 100644 index 00000000..32ef8f98 --- /dev/null +++ b/tests/data/cfg/test_no_preferences.yaml @@ -0,0 +1,2 @@ +test: + include: UNDEFINED_INCLUDE diff --git a/tests/data/cfg/test_preference_file_not_found.yaml b/tests/data/cfg/test_preference_file_not_found.yaml new file mode 100644 index 00000000..26932543 --- /dev/null +++ b/tests/data/cfg/test_preference_file_not_found.yaml @@ -0,0 +1 @@ +include: NOT_FOUND diff --git a/tests/data/cfg/test_sub_parent_includesA.yaml b/tests/data/cfg/test_sub_parent_includesA.yaml new file mode 100644 index 00000000..61efb271 --- /dev/null +++ b/tests/data/cfg/test_sub_parent_includesA.yaml @@ -0,0 +1,2 @@ +a: + include: sub/test_sub_parent_includesB.yaml diff --git a/tests/data/cfg/d.yaml b/tests/data/cfg/test_sub_parent_includesD.yaml similarity index 100% rename from tests/data/cfg/d.yaml rename to tests/data/cfg/test_sub_parent_includesD.yaml diff --git a/tests/test_cfg.py b/tests/test_cfg.py index 44736765..2c206670 100644 --- a/tests/test_cfg.py +++ b/tests/test_cfg.py @@ -4,32 +4,48 @@ from pyefis.cfg import from_yaml +def test_missing_path(): + with pytest.raises(SyntaxError): + d = from_yaml(fname='missing_path.yaml') def test_sub_parent_includes(): - d = from_yaml('tests/data/cfg/a.yaml') - assert d == {'a': {'b': {'c': {'done': True}}, 'b2': {'d': {'end': True}}}} + a = from_yaml('tests/data/cfg/test_sub_parent_includesA.yaml') + assert a == {'a': {'b': {'c': {'done': True}}, 'b2': {'d': {'end': True}}}} def test_array_and_preferences(): - e = from_yaml('tests/data/cfg/array_of_includes.yaml', preferences={"includes": {"PREFERENCEA": "PREFERENCEA.yaml"}}) - assert e == {'ARRAYS': {'includea': True, 'PREFERENCEA': True}} + b = from_yaml('tests/data/cfg/test_array_and_preferences.yaml', preferences={"includes": {"PREFERENCEA": "test_array_and_preferencesA.yaml","PREFERENCEB": "test_array_and_preferencesB.yaml"}}) + assert b == {'ARRAYS': {'includeC': True, 'includeA': True, 'includeB': True}} def test_data_type(): - with pytest.raises(Exception): - d = from_yaml('tests/data/cfg/not_string.yaml') + with pytest.raises(SyntaxError): + c = from_yaml('tests/data/cfg/test_data_type.yaml') def test_loop_detection_exception(): - with pytest.raises(Exception): - d = from_yaml('tests/data/cfg/loop1.yaml') - -# We want to test: -# Various loop exceptions -# -# include: filename -# include: -# - filename1: -# - filename2: -# include: PREFERENCE_NAME -# various uses of 'item' -# include from root or from same sub-folder -# include from another folder + with pytest.raises(RecursionError): + d = from_yaml('tests/data/cfg/test_loop_detection_exception.yaml') + +def test_preference_file_not_found(): + with pytest.raises(FileNotFoundError): + e = from_yaml('tests/data/cfg/test_preference_file_not_found.yaml',preferences={"includes":{"NOT_FOUND":"preference_not_found.yaml"}}) + +def test_no_preferences(): + with pytest.raises(FileNotFoundError): + e = from_yaml('tests/data/cfg/test_no_preferences.yaml') + +def test_list_include_missing_items(): + with pytest.raises(SyntaxError): + e = from_yaml('tests/data/cfg/test_list_include_missing_items.yaml') + +def test_list_include_missing_items_via_preferences(): + with pytest.raises(SyntaxError): + e = from_yaml('tests/data/cfg/test_list_include_missing_items_pref.yaml',preferences={"includes":{"PREFERENCED": "test_list_include_missing_items_via_preferencesD.yaml","PREFERENCEE": "preferencee.yaml"}}) + +def test_list_include_missing_items_via_preferences_nested(): + with pytest.raises(SyntaxError): + e = from_yaml('tests/data/cfg/test_list_include_missing_items_pref_nested.yaml',preferences={"includes":{"PREFERENCEF": "test_list_include_missing_items_via_preferences_nestedF.yaml","PREFERENCEG": "test_list_include_missing_items_via_preferences_nestedG.yaml"}}) + +def test_list_bpath_preference(): + e = from_yaml('tests/data/cfg/test_list_bpath_preference.yaml', preferences={"includes":{"A":"sub/sub2/test_list_bpath_preferenceA.yaml","B":"test_list_bpath_preferenceB.yaml","C":"test_list_bpath_preferenceC.yaml"}}) + assert e == {'test': [{'b_id': 5}, {'c_id_is': 5}, 5, 'Ten']} + diff --git a/tests/test_version.py b/tests/test_version.py new file mode 100644 index 00000000..d8b26e9f --- /dev/null +++ b/tests/test_version.py @@ -0,0 +1,4 @@ +import pyefis.version + +def test_pyefis_version(): + assert pyefis.__version__ is not None From a98ffe5c7d96521d36915bc665fa19ab9dff74a6 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Sun, 9 Jun 2024 14:33:12 -0400 Subject: [PATCH 20/60] Updated pyefis.cfg with better exceptions, fixed nested list bug --- src/pyefis/cfg.py | 59 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/src/pyefis/cfg.py b/src/pyefis/cfg.py index fcd03411..dbff24c3 100644 --- a/src/pyefis/cfg.py +++ b/src/pyefis/cfg.py @@ -1,13 +1,37 @@ import yaml import os -def from_yaml(fname,bpath=None,cfg=None,bc=[],preferences=None): +# allows using include preferences and include: keys to include yaml files inside yaml files +# While it does support including includes within includes, it does not support the include: key being nested deeply. +# +# Rxamples: +# include: some_file.yaml <- supported +# +# test: +# - include: some.yaml <- supported, when inside a list the included yaml needs to return a list inside items {items:[]} +# +# testing: +# include: some.yaml <- supported, the keys inside some.yaml will be nested under testing +# +# testing: +# deep: +# include: some.yaml <- not supported +# +# +# Thi was made to split monolithic files into smaller includable sections +# Allowing users to easily swap sections in/out to configure the screens to their liking + +def from_yaml(fname,bpath=None,cfg=None,bc=None,preferences=None): + if bc == None: + bc = list() bc.append(fname) if len(bc) > 500: import pprint - raise Exception(f"{pprint.pformat(bc)}\nPotential loop detected inside yaml includes, the breadcrumbs above might help detect where the issue is") + raise RecursionError(f"{pprint.pformat(bc)}\nPotential loop detected inside yaml includes, the breadcrumbs above might help detect where the issue is") fpath = os.path.dirname(fname) + if not cfg and not fpath: + raise SyntaxError(f"The filename '{fname}', must include the path, not just the filename") if not cfg: # cfg only populated to process nested data if not bpath: bpath = fpath @@ -23,7 +47,7 @@ def from_yaml(fname,bpath=None,cfg=None,bc=[],preferences=None): elif isinstance(val, list): files = val else: - raise Exception(f"#include in {fname} must be string or array") + raise SyntaxError(f"#include in {fname} must be string or array") # Process include(s) for f in files: # Check if file relative to current file @@ -33,22 +57,24 @@ def from_yaml(fname,bpath=None,cfg=None,bc=[],preferences=None): ifile = bpath + '/' + f if not os.path.exists(ifile): # Check preferences - if 'includes' in preferences: + if preferences != None and 'includes' in preferences: pfile = preferences['includes'].get(f,False) if pfile: ifile = fpath + '/' + pfile if not os.path.exists(ifile): ifile = bpath + '/' + pfile if not os.path.exists(ifile): - raise Exception(f"Cannot find include: {f}") + raise FileNotFoundError(f"Cannot find include: {f}") else: - raise Exception(f"Cannot find include: {f}") + raise FileNotFoundError(f"Cannot find include: {f}") sub = from_yaml(ifile, bpath,bc=bc,preferences=preferences) if hasattr(sub,'items'): for k, v in sub.items(): new[k] = v else: - raise Exception(f"Include {val} from {fname} is invalid") + # Not sure if this is correct error text, also not sure how to get here because I think one can only return a dict from base yaml + # Even bad syntax never gets here + raise SyntaxError(f"Error in {fname}\nWhen including list items they need listed under 'items:' in the include file") elif isinstance(val, dict): new[key] = from_yaml(fname,bpath,val,bc=bc,preferences=preferences) elif isinstance(val, list): @@ -70,17 +96,24 @@ def from_yaml(fname,bpath=None,cfg=None,bc=[],preferences=None): if not os.path.exists(ifile): ifile = bpath + '/' + pfile if not os.path.exists(ifile): - raise Exception(f"Cannot find include: {f}") + raise FileNotFoundError(f"Cannot find include: {f}") else: - raise Exception(f"Cannot find include: {f}") - with open(ifile) as cf: - litems = yaml.safe_load(cf) + raise FileNotFoundError(f"Cannot find include: {f}") + #with open(ifile) as cf: + litems = from_yaml(ifile,bpath,bc=bc,preferences=preferences) #yaml.safe_load(cf) if 'items' in litems: if litems['items'] != None: for a in litems['items']: - new[key].append(a) + # What about nested things? + ap = a + if isinstance(a,dict): + ap = from_yaml(ifile,bpath,a,bc=bc,preferences=preferences) + if isinstance(litems['items'], dict): + new[key].append({ap:litems['items'][ap]}) + else: + new[key].append(ap) else: - raise Exception(f"Error in {ifile}\nWhen including list items they need listed under 'items:' in the include file") + raise SyntaxError(f"Error in {ifile}\nWhen including list items they need listed under 'items:' in the include file") else: new[key].append(l) else: From b4cea80b5f3ac1ae0675e765c0ac3ae936d8b869 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Sun, 9 Jun 2024 14:42:18 -0400 Subject: [PATCH 21/60] ran black on tests, added missing test files --- src/pyefis/cfg.py | 106 +++++++++++------- tests/common/test_init.py | 7 +- .../cfg/test_list_include_missing_itemsA.yaml | 1 + .../test_list_include_missing_items_pref.yaml | 3 + ...ist_include_missing_items_pref_nested.yaml | 2 + tests/hmi/test_actionclass.py | 32 ++++-- tests/hmi/test_functions.py | 15 ++- tests/instruments/misc/test_misc.py | 33 +++--- tests/test_cfg.py | 76 ++++++++++--- tests/test_version.py | 1 + 10 files changed, 187 insertions(+), 89 deletions(-) create mode 100644 tests/data/cfg/test_list_include_missing_itemsA.yaml create mode 100644 tests/data/cfg/test_list_include_missing_items_pref.yaml create mode 100644 tests/data/cfg/test_list_include_missing_items_pref_nested.yaml diff --git a/src/pyefis/cfg.py b/src/pyefis/cfg.py index dbff24c3..0643b3ec 100644 --- a/src/pyefis/cfg.py +++ b/src/pyefis/cfg.py @@ -21,29 +21,36 @@ # Thi was made to split monolithic files into smaller includable sections # Allowing users to easily swap sections in/out to configure the screens to their liking -def from_yaml(fname,bpath=None,cfg=None,bc=None,preferences=None): + +def from_yaml(fname, bpath=None, cfg=None, bc=None, preferences=None): if bc == None: bc = list() bc.append(fname) if len(bc) > 500: import pprint - raise RecursionError(f"{pprint.pformat(bc)}\nPotential loop detected inside yaml includes, the breadcrumbs above might help detect where the issue is") - + + raise RecursionError( + f"{pprint.pformat(bc)}\nPotential loop detected inside yaml includes, the breadcrumbs above might help detect where the issue is" + ) + fpath = os.path.dirname(fname) if not cfg and not fpath: - raise SyntaxError(f"The filename '{fname}', must include the path, not just the filename") + raise SyntaxError( + f"The filename '{fname}', must include the path, not just the filename" + ) if not cfg: # cfg only populated to process nested data - if not bpath: bpath = fpath + if not bpath: + bpath = fpath with open(fname) as cf: cfg = yaml.safe_load(cf) new = {} - if hasattr(cfg,'items'): + if hasattr(cfg, "items"): for key, val in cfg.items(): - if key == 'include': + if key == "include": if isinstance(val, str): - files = [ val ] + files = [val] elif isinstance(val, list): files = val else: @@ -51,75 +58,92 @@ def from_yaml(fname,bpath=None,cfg=None,bc=None,preferences=None): # Process include(s) for f in files: # Check if file relative to current file - ifile = fpath + '/' + f + ifile = fpath + "/" + f if not os.path.exists(ifile): # Use base path - ifile = bpath + '/' + f + ifile = bpath + "/" + f if not os.path.exists(ifile): # Check preferences - if preferences != None and 'includes' in preferences: - pfile = preferences['includes'].get(f,False) + if preferences != None and "includes" in preferences: + pfile = preferences["includes"].get(f, False) if pfile: - ifile = fpath + '/' + pfile + ifile = fpath + "/" + pfile if not os.path.exists(ifile): - ifile = bpath + '/' + pfile + ifile = bpath + "/" + pfile if not os.path.exists(ifile): - raise FileNotFoundError(f"Cannot find include: {f}") + raise FileNotFoundError( + f"Cannot find include: {f}" + ) else: raise FileNotFoundError(f"Cannot find include: {f}") - sub = from_yaml(ifile, bpath,bc=bc,preferences=preferences) - if hasattr(sub,'items'): - for k, v in sub.items(): - new[k] = v + sub = from_yaml(ifile, bpath, bc=bc, preferences=preferences) + if hasattr(sub, "items"): + for k, v in sub.items(): + new[k] = v else: # Not sure if this is correct error text, also not sure how to get here because I think one can only return a dict from base yaml # Even bad syntax never gets here - raise SyntaxError(f"Error in {fname}\nWhen including list items they need listed under 'items:' in the include file") + raise SyntaxError( + f"Error in {fname}\nWhen including list items they need listed under 'items:' in the include file" + ) elif isinstance(val, dict): - new[key] = from_yaml(fname,bpath,val,bc=bc,preferences=preferences) + new[key] = from_yaml(fname, bpath, val, bc=bc, preferences=preferences) elif isinstance(val, list): new[key] = [] # Included array elements for l in val: if isinstance(l, dict): - if 'include' in l: - ifile = fpath + '/' + l['include'] + if "include" in l: + ifile = fpath + "/" + l["include"] if not os.path.exists(ifile): # Use base path - ifile = bpath + '/' + l['include'] + ifile = bpath + "/" + l["include"] if not os.path.exists(ifile): # Check preferences - if 'includes' in preferences: - pfile = preferences['includes'].get(l['include'],False) + if "includes" in preferences: + pfile = preferences["includes"].get( + l["include"], False + ) if pfile: - ifile = fpath + '/' + pfile + ifile = fpath + "/" + pfile if not os.path.exists(ifile): - ifile = bpath + '/' + pfile + ifile = bpath + "/" + pfile if not os.path.exists(ifile): - raise FileNotFoundError(f"Cannot find include: {f}") + raise FileNotFoundError( + f"Cannot find include: {f}" + ) else: raise FileNotFoundError(f"Cannot find include: {f}") - #with open(ifile) as cf: - litems = from_yaml(ifile,bpath,bc=bc,preferences=preferences) #yaml.safe_load(cf) - if 'items' in litems: - if litems['items'] != None: - for a in litems['items']: + # with open(ifile) as cf: + litems = from_yaml( + ifile, bpath, bc=bc, preferences=preferences + ) # yaml.safe_load(cf) + if "items" in litems: + if litems["items"] != None: + for a in litems["items"]: # What about nested things? ap = a - if isinstance(a,dict): - ap = from_yaml(ifile,bpath,a,bc=bc,preferences=preferences) - if isinstance(litems['items'], dict): - new[key].append({ap:litems['items'][ap]}) + if isinstance(a, dict): + ap = from_yaml( + ifile, + bpath, + a, + bc=bc, + preferences=preferences, + ) + if isinstance(litems["items"], dict): + new[key].append({ap: litems["items"][ap]}) else: new[key].append(ap) else: - raise SyntaxError(f"Error in {ifile}\nWhen including list items they need listed under 'items:' in the include file") + raise SyntaxError( + f"Error in {ifile}\nWhen including list items they need listed under 'items:' in the include file" + ) else: new[key].append(l) else: new[key].append(l) else: - #Save existing + # Save existing new[key] = val return new - diff --git a/tests/common/test_init.py b/tests/common/test_init.py index 6374f382..4d2af0cf 100644 --- a/tests/common/test_init.py +++ b/tests/common/test_init.py @@ -1,21 +1,26 @@ import pytest from pyefis.common import bounds + def test_bounds_below_min(): assert bounds(0, 10, -5) == 0 + def test_bounds_above_max(): assert bounds(0, 10, 15) == 10 + def test_bounds_within_range(): assert bounds(0, 10, 5) == 5 + def test_bounds_on_min(): assert bounds(0, 10, 0) == 0 + def test_bounds_on_max(): assert bounds(0, 10, 10) == 10 + if __name__ == "__main__": pytest.main() - diff --git a/tests/data/cfg/test_list_include_missing_itemsA.yaml b/tests/data/cfg/test_list_include_missing_itemsA.yaml new file mode 100644 index 00000000..2448b072 --- /dev/null +++ b/tests/data/cfg/test_list_include_missing_itemsA.yaml @@ -0,0 +1 @@ +No_items_at_root: true diff --git a/tests/data/cfg/test_list_include_missing_items_pref.yaml b/tests/data/cfg/test_list_include_missing_items_pref.yaml new file mode 100644 index 00000000..a0a195ac --- /dev/null +++ b/tests/data/cfg/test_list_include_missing_items_pref.yaml @@ -0,0 +1,3 @@ +list_of_items: + - test: true + - include: PREFERENCED diff --git a/tests/data/cfg/test_list_include_missing_items_pref_nested.yaml b/tests/data/cfg/test_list_include_missing_items_pref_nested.yaml new file mode 100644 index 00000000..cc8e68c3 --- /dev/null +++ b/tests/data/cfg/test_list_include_missing_items_pref_nested.yaml @@ -0,0 +1,2 @@ +wrong_items: + - include: PREFERENCEF diff --git a/tests/hmi/test_actionclass.py b/tests/hmi/test_actionclass.py index 19b92de1..9342890b 100644 --- a/tests/hmi/test_actionclass.py +++ b/tests/hmi/test_actionclass.py @@ -1,51 +1,62 @@ import pytest from PyQt5.QtCore import pyqtSignal, pyqtBoundSignal from PyQt5.QtWidgets import QApplication -from unittest import mock #MagicMock, mock.patch -from pyefis.hmi import actionclass #ActionClass +from unittest import mock # MagicMock, mock.patch +from pyefis.hmi import actionclass # ActionClass + # Ensure QApplication instance -@pytest.fixture#(scope="module", autouse=True) +@pytest.fixture def app(qtbot): test_app = QApplication.instance() if test_app is None: test_app = QApplication([]) return test_app + def test_initialization(qtbot): action_class = actionclass.ActionClass() assert action_class.signalMap is not None assert "set airspeed mode" in action_class.signalMap -@mock.patch('pyefis.hmi.functions.setValue') + +@mock.patch("pyefis.hmi.functions.setValue") def test_trigger_function(mock_setValue, qtbot): - action_class = actionclass.ActionClass() + action_class = actionclass.ActionClass() # Mocking the entire functions module mock_setValue.return_value = None - #mock_db.get_item.return_value = None # Mocking pyavtools.fix.db as well - + # mock_db.get_item.return_value = None # Mocking pyavtools.fix.db as well + action_class.trigger("set value", "test") # Asserting that setValue is called mock_setValue.assert_called_once_with("test") + def test_trigger_signal(qtbot): action_class = actionclass.ActionClass() mock_signal = mock.MagicMock(spec=pyqtBoundSignal) - action_class.signalMap["set airspeed mode"] = mock_signal # mock.patching emit directly + action_class.signalMap["set airspeed mode"] = ( + mock_signal # mock.patching emit directly + ) action_class.trigger("set airspeed mode", "test") - mock_signal.emit.assert_called_once_with("test") # Use assert_called_once_with on mock_signal directly + mock_signal.emit.assert_called_once_with( + "test" + ) # Use assert_called_once_with on mock_signal directly + def test_find_action_exists(qtbot): action_class = actionclass.ActionClass() action = action_class.findAction("set airspeed mode") assert action == action_class.setAirspeedMode - + + def test_find_action_not_exists(qtbot): action_class = actionclass.ActionClass() action = action_class.findAction("nonexistent action") assert action is None + def test_trigger_eval(qtbot): action_class = actionclass.ActionClass() mock_eval = mock.MagicMock() @@ -53,5 +64,6 @@ def test_trigger_eval(qtbot): action_class.trigger("evaluate", "print('test')") mock_eval.assert_called_once_with("print('test')") + if __name__ == "__main__": pytest.main() diff --git a/tests/hmi/test_functions.py b/tests/hmi/test_functions.py index 08c75ae3..fe785a63 100644 --- a/tests/hmi/test_functions.py +++ b/tests/hmi/test_functions.py @@ -1,13 +1,15 @@ import pytest from unittest.mock import MagicMock, patch -import pyefis.hmi.functions +import pyefis.hmi.functions + # Mock the fix module @pytest.fixture def fix_mock(): - with patch('pyefis.hmi.functions.fix') as fix_mock: + with patch("pyefis.hmi.functions.fix") as fix_mock: yield fix_mock + # Test for setValue function def test_setValue(fix_mock): # Mocking the arg for setValue @@ -16,7 +18,7 @@ def test_setValue(fix_mock): item_mock = MagicMock() item_mock.value = None fix_mock.db.get_item.return_value = item_mock - + # Calling the function pyefis.hmi.functions.setValue(arg) @@ -25,6 +27,7 @@ def test_setValue(fix_mock): assert item_mock.value == "value" assert item_mock.output_value.called + # Test for changeValue function def test_changeValue(fix_mock): # Mocking the arg for changeValue @@ -34,7 +37,7 @@ def test_changeValue(fix_mock): item_mock.value = 1 item_mock.dtype.return_value = 1 fix_mock.db.get_item.return_value = item_mock - + # Calling the function pyefis.hmi.functions.changeValue(arg) @@ -44,6 +47,7 @@ def test_changeValue(fix_mock): assert item_mock.dtype.called assert item_mock.output_value.called + # Test for toggleBool function def test_toggleBool(fix_mock): # Mocking the arg for toggleBool @@ -52,7 +56,7 @@ def test_toggleBool(fix_mock): item_mock = MagicMock() item_mock.value = False fix_mock.db.get_item.return_value = item_mock - + # Calling the function pyefis.hmi.functions.toggleBool(arg) @@ -60,4 +64,3 @@ def test_toggleBool(fix_mock): fix_mock.db.get_item.assert_called_once_with("key") assert item_mock.value == True assert item_mock.output_value.called - diff --git a/tests/instruments/misc/test_misc.py b/tests/instruments/misc/test_misc.py index 603d1d71..65fbc46c 100644 --- a/tests/instruments/misc/test_misc.py +++ b/tests/instruments/misc/test_misc.py @@ -18,22 +18,23 @@ def app(qtbot): test_app = QApplication([]) return test_app -def test_save_screenshot(qtbot,request): + +def test_save_screenshot(qtbot, request): widget1 = StaticText(text="Testing!") - widget1.font_mask = "XXXXXXXX" + widget1.font_mask = "XXXXXXXX" widget1.color = QColor(Qt.black) - #qtbot.addWidget(widget1) - widget1.move(0,0) + widget1.move(0, 0) widget1.resize(100, 50) - #qtbot.waitExposed(widget1) - #assert widget1.width() == 100 - #assert widget1.height() == 10 qtbot.addWidget(widget1) widget1.show() qtbot.waitExposed(widget1) - path = qtbot.screenshot(widget1,"instruments-misc-test_misc") + path = qtbot.screenshot(widget1, "instruments-misc-test_misc") qtbot.wait(500) - os.rename(path, request.config.rootdir + "/extras/extras/test_results/instruments-misc-test_misc-StaticText.png") + os.rename( + path, + request.config.rootdir + + "/extras/extras/test_results/instruments-misc-test_misc-StaticText.png", + ) def test_static_text_default(qtbot): @@ -46,8 +47,7 @@ def test_static_text_default(qtbot): widget.show() assert widget.isVisible() - #qtbot.waitForWindowShown(widget) - #time.sleep(3) + def test_static_text_resize(qtbot): widget = StaticText(text="Test", fontsize=1.0) @@ -58,6 +58,7 @@ def test_static_text_resize(qtbot): assert widget.width() == 200 assert widget.height() == 100 + def test_value_display_default(qtbot): widget = ValueDisplay() qtbot.addWidget(widget) @@ -72,6 +73,7 @@ def test_value_display_default(qtbot): widget.show() assert widget.isVisible() + def test_value_display_set_value(qtbot): widget = ValueDisplay() qtbot.addWidget(widget) @@ -82,7 +84,8 @@ def test_value_display_set_value(qtbot): widget.setValue(-3.14) assert widget.getValue() == -3.14 -@mock.patch('pyefis.instruments.misc.fix') + +@mock.patch("pyefis.instruments.misc.fix") def test_value_display_flags(mock_fix, qtbot): mock_item = mock.Mock() mock_item.value = 100.0 @@ -101,11 +104,11 @@ def test_value_display_flags(mock_fix, qtbot): widget.oldFlag(True) assert widget.old == True - assert widget.getValueText() == '100.0' + assert widget.getValueText() == "100.0" widget.badFlag(True) assert widget.bad == True - assert widget.getValueText() == '100.0' + assert widget.getValueText() == "100.0" widget.failFlag(True) assert widget.fail == True @@ -114,6 +117,6 @@ def test_value_display_flags(mock_fix, qtbot): widget.annunciateFlag(True) assert widget.annunciate == True + if __name__ == "__main__": pytest.main() - diff --git a/tests/test_cfg.py b/tests/test_cfg.py index 2c206670..9597c674 100644 --- a/tests/test_cfg.py +++ b/tests/test_cfg.py @@ -6,46 +6,90 @@ def test_missing_path(): with pytest.raises(SyntaxError): - d = from_yaml(fname='missing_path.yaml') + d = from_yaml(fname="missing_path.yaml") + def test_sub_parent_includes(): - a = from_yaml('tests/data/cfg/test_sub_parent_includesA.yaml') - assert a == {'a': {'b': {'c': {'done': True}}, 'b2': {'d': {'end': True}}}} + a = from_yaml("tests/data/cfg/test_sub_parent_includesA.yaml") + assert a == {"a": {"b": {"c": {"done": True}}, "b2": {"d": {"end": True}}}} + def test_array_and_preferences(): - b = from_yaml('tests/data/cfg/test_array_and_preferences.yaml', preferences={"includes": {"PREFERENCEA": "test_array_and_preferencesA.yaml","PREFERENCEB": "test_array_and_preferencesB.yaml"}}) - assert b == {'ARRAYS': {'includeC': True, 'includeA': True, 'includeB': True}} + b = from_yaml( + "tests/data/cfg/test_array_and_preferences.yaml", + preferences={ + "includes": { + "PREFERENCEA": "test_array_and_preferencesA.yaml", + "PREFERENCEB": "test_array_and_preferencesB.yaml", + } + }, + ) + assert b == {"ARRAYS": {"includeC": True, "includeA": True, "includeB": True}} + def test_data_type(): with pytest.raises(SyntaxError): - c = from_yaml('tests/data/cfg/test_data_type.yaml') + c = from_yaml("tests/data/cfg/test_data_type.yaml") + def test_loop_detection_exception(): with pytest.raises(RecursionError): - d = from_yaml('tests/data/cfg/test_loop_detection_exception.yaml') + d = from_yaml("tests/data/cfg/test_loop_detection_exception.yaml") + def test_preference_file_not_found(): with pytest.raises(FileNotFoundError): - e = from_yaml('tests/data/cfg/test_preference_file_not_found.yaml',preferences={"includes":{"NOT_FOUND":"preference_not_found.yaml"}}) + e = from_yaml( + "tests/data/cfg/test_preference_file_not_found.yaml", + preferences={"includes": {"NOT_FOUND": "preference_not_found.yaml"}}, + ) + def test_no_preferences(): with pytest.raises(FileNotFoundError): - e = from_yaml('tests/data/cfg/test_no_preferences.yaml') + e = from_yaml("tests/data/cfg/test_no_preferences.yaml") + def test_list_include_missing_items(): with pytest.raises(SyntaxError): - e = from_yaml('tests/data/cfg/test_list_include_missing_items.yaml') + e = from_yaml("tests/data/cfg/test_list_include_missing_items.yaml") + def test_list_include_missing_items_via_preferences(): with pytest.raises(SyntaxError): - e = from_yaml('tests/data/cfg/test_list_include_missing_items_pref.yaml',preferences={"includes":{"PREFERENCED": "test_list_include_missing_items_via_preferencesD.yaml","PREFERENCEE": "preferencee.yaml"}}) + e = from_yaml( + "tests/data/cfg/test_list_include_missing_items_pref.yaml", + preferences={ + "includes": { + "PREFERENCED": "test_list_include_missing_items_via_preferencesD.yaml", + "PREFERENCEE": "preferencee.yaml", + } + }, + ) + def test_list_include_missing_items_via_preferences_nested(): with pytest.raises(SyntaxError): - e = from_yaml('tests/data/cfg/test_list_include_missing_items_pref_nested.yaml',preferences={"includes":{"PREFERENCEF": "test_list_include_missing_items_via_preferences_nestedF.yaml","PREFERENCEG": "test_list_include_missing_items_via_preferences_nestedG.yaml"}}) - -def test_list_bpath_preference(): - e = from_yaml('tests/data/cfg/test_list_bpath_preference.yaml', preferences={"includes":{"A":"sub/sub2/test_list_bpath_preferenceA.yaml","B":"test_list_bpath_preferenceB.yaml","C":"test_list_bpath_preferenceC.yaml"}}) - assert e == {'test': [{'b_id': 5}, {'c_id_is': 5}, 5, 'Ten']} + e = from_yaml( + "tests/data/cfg/test_list_include_missing_items_pref_nested.yaml", + preferences={ + "includes": { + "PREFERENCEF": "test_list_include_missing_items_via_preferences_nestedF.yaml", + "PREFERENCEG": "test_list_include_missing_items_via_preferences_nestedG.yaml", + } + }, + ) +def test_list_bpath_preference(): + e = from_yaml( + "tests/data/cfg/test_list_bpath_preference.yaml", + preferences={ + "includes": { + "A": "sub/sub2/test_list_bpath_preferenceA.yaml", + "B": "test_list_bpath_preferenceB.yaml", + "C": "test_list_bpath_preferenceC.yaml", + } + }, + ) + assert e == {"test": [{"b_id": 5}, {"c_id_is": 5}, 5, "Ten"]} diff --git a/tests/test_version.py b/tests/test_version.py index d8b26e9f..de8dc2eb 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -1,4 +1,5 @@ import pyefis.version + def test_pyefis_version(): assert pyefis.__version__ is not None From cfb444580369220a8fa439a08e6a0aa795744308 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Sun, 9 Jun 2024 15:25:12 -0400 Subject: [PATCH 22/60] 100% coverage for pyefis.cfg, removed unneeded code --- src/pyefis/cfg.py | 10 ++-------- .../test_preference_file_not_found.yaml | 0 .../sub/test_preference_file_not_found_list.yaml | 2 ++ tests/test_cfg.py | 16 +++++++++++++++- 4 files changed, 19 insertions(+), 9 deletions(-) rename tests/data/cfg/{ => sub}/test_preference_file_not_found.yaml (100%) create mode 100644 tests/data/cfg/sub/test_preference_file_not_found_list.yaml diff --git a/src/pyefis/cfg.py b/src/pyefis/cfg.py index 0643b3ec..10384a55 100644 --- a/src/pyefis/cfg.py +++ b/src/pyefis/cfg.py @@ -80,12 +80,6 @@ def from_yaml(fname, bpath=None, cfg=None, bc=None, preferences=None): if hasattr(sub, "items"): for k, v in sub.items(): new[k] = v - else: - # Not sure if this is correct error text, also not sure how to get here because I think one can only return a dict from base yaml - # Even bad syntax never gets here - raise SyntaxError( - f"Error in {fname}\nWhen including list items they need listed under 'items:' in the include file" - ) elif isinstance(val, dict): new[key] = from_yaml(fname, bpath, val, bc=bc, preferences=preferences) elif isinstance(val, list): @@ -110,10 +104,10 @@ def from_yaml(fname, bpath=None, cfg=None, bc=None, preferences=None): ifile = bpath + "/" + pfile if not os.path.exists(ifile): raise FileNotFoundError( - f"Cannot find include: {f}" + f"Cannot find include: {pfile}" ) else: - raise FileNotFoundError(f"Cannot find include: {f}") + raise FileNotFoundError(f"Cannot find include: {ifile}") # with open(ifile) as cf: litems = from_yaml( ifile, bpath, bc=bc, preferences=preferences diff --git a/tests/data/cfg/test_preference_file_not_found.yaml b/tests/data/cfg/sub/test_preference_file_not_found.yaml similarity index 100% rename from tests/data/cfg/test_preference_file_not_found.yaml rename to tests/data/cfg/sub/test_preference_file_not_found.yaml diff --git a/tests/data/cfg/sub/test_preference_file_not_found_list.yaml b/tests/data/cfg/sub/test_preference_file_not_found_list.yaml new file mode 100644 index 00000000..3bff62dc --- /dev/null +++ b/tests/data/cfg/sub/test_preference_file_not_found_list.yaml @@ -0,0 +1,2 @@ +list: + - include: NOT_FOUND diff --git a/tests/test_cfg.py b/tests/test_cfg.py index 9597c674..7fe4af24 100644 --- a/tests/test_cfg.py +++ b/tests/test_cfg.py @@ -40,10 +40,24 @@ def test_loop_detection_exception(): def test_preference_file_not_found(): with pytest.raises(FileNotFoundError): e = from_yaml( - "tests/data/cfg/test_preference_file_not_found.yaml", + "tests/data/cfg/sub/test_preference_file_not_found.yaml", preferences={"includes": {"NOT_FOUND": "preference_not_found.yaml"}}, ) +def test_preference_file_not_found_list(): + with pytest.raises(FileNotFoundError): + e = from_yaml( + "tests/data/cfg/sub/test_preference_file_not_found_list.yaml", + preferences={"includes": {"NOT_FOUND": "preference_not_found.yaml"}}, + ) + +def test_preference_file_not_found_list_no_includes(): + with pytest.raises(FileNotFoundError): + e = from_yaml( + "tests/data/cfg/sub/test_preference_file_not_found_list.yaml", + preferences={"includes_typo": {"NOT_FOUND": "preference_not_found.yaml"}}, + ) + def test_no_preferences(): with pytest.raises(FileNotFoundError): From e88e10b81db7159bca1a69ee6e5e26a2f26cec00 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Sun, 9 Jun 2024 19:43:32 -0400 Subject: [PATCH 23/60] Added tst for pyefis.hmi init --- tests/hmi/test_init.py | 51 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 tests/hmi/test_init.py diff --git a/tests/hmi/test_init.py b/tests/hmi/test_init.py new file mode 100644 index 00000000..5d3ecb9a --- /dev/null +++ b/tests/hmi/test_init.py @@ -0,0 +1,51 @@ +import pytest +from unittest.mock import patch, MagicMock +import logging +from pyefis.hmi import initialize, actionclass, data + +# Fixture to patch and reset global actions variable +@pytest.fixture(autouse=True) +def patch_globals(): + with patch('pyefis.hmi.actions', None): + yield + +# Fixture to patch the logging module +@pytest.fixture +def log_mock(): + with patch('pyefis.hmi.logging.getLogger') as logger_mock: + yield logger_mock + +def test_initialize_with_databindings(log_mock): + # Mock the actionclass.ActionClass + with patch('pyefis.hmi.actionclass.ActionClass', autospec=True) as mock_action_class: + # Mock the data.initialize function + with patch('pyefis.hmi.data.initialize') as mock_data_initialize: + # Sample config with databindings + config = { + "databindings": {"key": "value"} + } + # Call the initialize function with config + initialize(config) + # Check that logging was called correctly + log_mock.return_value.info.assert_called_once_with("Initializing Actions") + # Check that ActionClass was instantiated + mock_action_class.assert_called_once() + # Check that data.initialize was called with correct config + mock_data_initialize.assert_called_once_with(config["databindings"]) + +def test_initialize_without_databindings(log_mock): + # Mock the actionclass.ActionClass + with patch('pyefis.hmi.actionclass.ActionClass', autospec=True) as mock_action_class: + # Mock the data.initialize function + with patch('pyefis.hmi.data.initialize') as mock_data_initialize: + # Sample config without databindings + config = {} + # Call the initialize function with config + initialize(config) + # Check that logging was called correctly + log_mock.return_value.info.assert_called_once_with("Initializing Actions") + # Check that ActionClass was instantiated + mock_action_class.assert_called_once() + # Check that data.initialize was not called + mock_data_initialize.assert_not_called() + From 7f17f33fbd1808c15f65803e93b0af48464b5f9d Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Sun, 9 Jun 2024 19:45:57 -0400 Subject: [PATCH 24/60] Formatted pyefis.hmi init test with black --- src/pyefis/hmi/__init__.py | 1 + tests/hmi/test_init.py | 25 +++++++++++++++---------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/pyefis/hmi/__init__.py b/src/pyefis/hmi/__init__.py index b68942b0..b3b5fc42 100644 --- a/src/pyefis/hmi/__init__.py +++ b/src/pyefis/hmi/__init__.py @@ -24,6 +24,7 @@ actions = None from . import menu + def initialize(config): global actions log = logging.getLogger(__name__) diff --git a/tests/hmi/test_init.py b/tests/hmi/test_init.py index 5d3ecb9a..7ec2d600 100644 --- a/tests/hmi/test_init.py +++ b/tests/hmi/test_init.py @@ -3,27 +3,30 @@ import logging from pyefis.hmi import initialize, actionclass, data + # Fixture to patch and reset global actions variable @pytest.fixture(autouse=True) def patch_globals(): - with patch('pyefis.hmi.actions', None): + with patch("pyefis.hmi.actions", None): yield + # Fixture to patch the logging module @pytest.fixture def log_mock(): - with patch('pyefis.hmi.logging.getLogger') as logger_mock: + with patch("pyefis.hmi.logging.getLogger") as logger_mock: yield logger_mock + def test_initialize_with_databindings(log_mock): # Mock the actionclass.ActionClass - with patch('pyefis.hmi.actionclass.ActionClass', autospec=True) as mock_action_class: + with patch( + "pyefis.hmi.actionclass.ActionClass", autospec=True + ) as mock_action_class: # Mock the data.initialize function - with patch('pyefis.hmi.data.initialize') as mock_data_initialize: + with patch("pyefis.hmi.data.initialize") as mock_data_initialize: # Sample config with databindings - config = { - "databindings": {"key": "value"} - } + config = {"databindings": {"key": "value"}} # Call the initialize function with config initialize(config) # Check that logging was called correctly @@ -33,11 +36,14 @@ def test_initialize_with_databindings(log_mock): # Check that data.initialize was called with correct config mock_data_initialize.assert_called_once_with(config["databindings"]) + def test_initialize_without_databindings(log_mock): # Mock the actionclass.ActionClass - with patch('pyefis.hmi.actionclass.ActionClass', autospec=True) as mock_action_class: + with patch( + "pyefis.hmi.actionclass.ActionClass", autospec=True + ) as mock_action_class: # Mock the data.initialize function - with patch('pyefis.hmi.data.initialize') as mock_data_initialize: + with patch("pyefis.hmi.data.initialize") as mock_data_initialize: # Sample config without databindings config = {} # Call the initialize function with config @@ -48,4 +54,3 @@ def test_initialize_without_databindings(log_mock): mock_action_class.assert_called_once() # Check that data.initialize was not called mock_data_initialize.assert_not_called() - From 5d3ec9b4c2fcb7b9ef65d298b5031b71dafbf838 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Mon, 10 Jun 2024 17:09:18 -0400 Subject: [PATCH 25/60] instruments.misc now has 100% test coverage except for the try except: pass in setupGauges, not sure how to test those two lines --- src/pyefis/instruments/misc/__init__.py | 2 +- tests/instruments/misc/test_misc.py | 47 ++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/pyefis/instruments/misc/__init__.py b/src/pyefis/instruments/misc/__init__.py index 5e28c462..1de2ca73 100644 --- a/src/pyefis/instruments/misc/__init__.py +++ b/src/pyefis/instruments/misc/__init__.py @@ -217,7 +217,7 @@ def setupGauge(self): self.item.valueChanged[int].disconnect(self.setValue) self.item.valueChanged[bool].disconnect(self.setValue) self.item.valueChanged[str].disconnect(self.setValue) - except: + except TypeError: pass # One will probably fail all the time self.item.valueChanged[self.item.dtype].connect(self.setValue) diff --git a/tests/instruments/misc/test_misc.py b/tests/instruments/misc/test_misc.py index 65fbc46c..810a63d5 100644 --- a/tests/instruments/misc/test_misc.py +++ b/tests/instruments/misc/test_misc.py @@ -39,14 +39,19 @@ def test_save_screenshot(qtbot, request): def test_static_text_default(qtbot): widget = StaticText(text="Testing!") + widget.font_mask = "0.0" + widget.font_ghost_mask = "0.0" qtbot.addWidget(widget) assert widget.text == "Testing!" assert widget.font_family == "DejaVu Sans Condensed" assert widget.color == QColor(Qt.white) - + widget.resizeEvent(None) + widget.paintEvent(None) widget.show() + qtbot.waitExposed(widget) assert widget.isVisible() + assert widget.color.alpha() != 50 def test_static_text_resize(qtbot): @@ -54,6 +59,8 @@ def test_static_text_resize(qtbot): qtbot.addWidget(widget) widget.resize(200, 100) + widget.resizeEvent(None) + widget.paintEvent(None) qtbot.waitExposed(widget) assert widget.width() == 200 assert widget.height() == 100 @@ -73,6 +80,18 @@ def test_value_display_default(qtbot): widget.show() assert widget.isVisible() +def test_value_display_font_mask(qtbot): + widget = ValueDisplay() + widget.font_size = 100 + widget.font_mask = "0.0" + widget.font_ghost_mask = "0.0" + qtbot.addWidget(widget) + widget.resizeEvent(None) + widget.paintEvent(None) + widget.show() + qtbot.waitExposed(widget) + assert widget.isVisible() + assert widget.font_size != 100 def test_value_display_set_value(qtbot): widget = ValueDisplay() @@ -114,8 +133,34 @@ def test_value_display_flags(mock_fix, qtbot): assert widget.fail == True assert widget.getValueText() == "xxx" + widget.failFlag(False) widget.annunciateFlag(True) assert widget.annunciate == True + assert widget.textColor == widget.textAnnunciateColor + +@mock.patch("pyefis.instruments.misc.fix") +def test_set_db_key(mock_fix,qtbot): + mock_item = mock.MagicMock() + mock_item.value = 100.0 + mock_item.fail = True + mock_item.bad = False + mock_fix.db.get_item.return_value = mock_item + mock_setupGauge = mock.MagicMock() + widget = ValueDisplay() + qtbot.addWidget(widget) + widget.setDbkey('TEST') + mock_fix.db.get_item.assert_called_once_with("TEST") + mock_item.reportReceived.connect.assert_called_once_with(widget.setupGauge) + mock_item.annunciateChanged.connect.assert_called_once_with(widget.annunciateFlag) + mock_item.oldChanged.connect.assert_called_once_with(widget.oldFlag) + mock_item.badChanged.connect.assert_called_once_with(widget.badFlag) + mock_item.failChanged.connect.assert_called_once_with(widget.failFlag) + assert widget._dbkey == "TEST" + # These would only match if self.setupGauge() was called: + assert widget.fail == True + assert widget.bad == False +# assert mock_item. + if __name__ == "__main__": From c5db8150f1ec3b1a9285f251223fabadc305ee51 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Mon, 10 Jun 2024 21:55:33 -0400 Subject: [PATCH 26/60] Added mock_db that allows running the fix db without a six gateway, this will make testing easier since we do not need to constantly mock fix.db and deal with mocking signals. The trade off is slower testing because each test is running more code --- conftest.py | 15 ++++++++++++++ mock_db/__init__.py | 0 mock_db/client.py | 37 ++++++++++++++++++++++++++++++++++ mock_db/scheduler.py | 48 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+) create mode 100644 conftest.py create mode 100644 mock_db/__init__.py create mode 100644 mock_db/client.py create mode 100644 mock_db/scheduler.py diff --git a/conftest.py b/conftest.py new file mode 100644 index 00000000..fadb83eb --- /dev/null +++ b/conftest.py @@ -0,0 +1,15 @@ +import sys +import mock_db.client +import mock_db.scheduler +sys.modules["pyavtools.fix.client"] = mock_db.client +sys.modules["pyavtools.scheduler"] = mock_db.scheduler + + +import pyavtools.fix as fix +fix.initialize({"main":{"FixServer":"localhost","FixPort":"3490"}}) + +# stop waiting +fix.db.init_event.set() +# Define itms: +fix.db.define_item("TEST", "Testing key", 'float', 0, 100, '', 50000, "") +fix.db.set_value("TEST", 50) diff --git a/mock_db/__init__.py b/mock_db/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/mock_db/client.py b/mock_db/client.py new file mode 100644 index 00000000..ee2b3979 --- /dev/null +++ b/mock_db/client.py @@ -0,0 +1,37 @@ +from unittest import mock + + +class SendThread: + def __init__(self, sock, queue): + pass + + def run(self): + pass + + def stop(self): + pass + +class ClientThread: + def __init__(self, host, port, db): + super(ClientThread, self).__init__() + self.sendqueue = mock.MagicMock() + self.db = db # Main FIX database + + def handle_value(self, d): + pass + + def handle_request(self, d): + self.db.init_event.set() + + def run(self): + pass + + def stop(self): + pass + + def join(self): + pass + + def start(Self): + pass + diff --git a/mock_db/scheduler.py b/mock_db/scheduler.py new file mode 100644 index 00000000..fc62bcd1 --- /dev/null +++ b/mock_db/scheduler.py @@ -0,0 +1,48 @@ +class IntervalTimer: + def __init__(self, interval): + self.interval = interval + self.callbacks = [] + + def fire_timer(self): + pass + + def add_callback(self, func): + if func not in self.callbacks: + self.callbacks.append(func) + + def start(self): + pass + + def stop(self): + pass + + def interval(self): + return self.interval + +class ScheduleThread: + def __init__(self): + self.timers = [] + + def run(self): + self.timers.append(IntervalTimer(100)) + self.timers.append(IntervalTimer(500)) + self.timers.append(IntervalTimer(1000)) + + def stop(self): + pass + + def getTimer(self, interval): + for each in self.timers: + if each.timer.interval() == interval: + return each + + def start(self): + pass + +initialized=False +def initialize(): + global initialized + scheduler = ScheduleThread() + scheduler.start() + initialized=True + From eece5d809dfa5123eff78dbac4f9c883581d8fb2 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Tue, 11 Jun 2024 18:01:01 -0400 Subject: [PATCH 27/60] updated mock_db and instruments.misc tests --- conftest.py | 16 ++++++++++++---- mock_db/client.py | 3 ++- tests/instruments/misc/test_misc.py | 25 ++++++++++++++++--------- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/conftest.py b/conftest.py index fadb83eb..5f969b23 100644 --- a/conftest.py +++ b/conftest.py @@ -1,15 +1,23 @@ +# Patch pyavtools.fix so it can run in memory without a fix server import sys import mock_db.client import mock_db.scheduler sys.modules["pyavtools.fix.client"] = mock_db.client sys.modules["pyavtools.scheduler"] = mock_db.scheduler - +# import fix import pyavtools.fix as fix +# Init database fix.initialize({"main":{"FixServer":"localhost","FixPort":"3490"}}) -# stop waiting -fix.db.init_event.set() -# Define itms: +# TODO Add example setting aux values since needed on many tests +# Define some fix db items we can use in tests +# Define a key: +# key,desc, dtype(as a string), min, max, units, tol, aux fix.db.define_item("TEST", "Testing key", 'float', 0, 100, '', 50000, "") +# Set initial Value fix.db.set_value("TEST", 50) +# Bad and fail are true by default to set them False +fix.db.get_item("TEST").bad = False +fix.db.get_item("TEST").fail = False + diff --git a/mock_db/client.py b/mock_db/client.py index ee2b3979..b05a303a 100644 --- a/mock_db/client.py +++ b/mock_db/client.py @@ -16,12 +16,13 @@ def __init__(self, host, port, db): super(ClientThread, self).__init__() self.sendqueue = mock.MagicMock() self.db = db # Main FIX database + self.db.init_event.set() def handle_value(self, d): pass def handle_request(self, d): - self.db.init_event.set() + pass def run(self): pass diff --git a/tests/instruments/misc/test_misc.py b/tests/instruments/misc/test_misc.py index 810a63d5..486d21a8 100644 --- a/tests/instruments/misc/test_misc.py +++ b/tests/instruments/misc/test_misc.py @@ -9,6 +9,7 @@ import time import subprocess import os +import pyavtools.fix as fix @pytest.fixture @@ -95,22 +96,28 @@ def test_value_display_font_mask(qtbot): def test_value_display_set_value(qtbot): widget = ValueDisplay() + fix.db.set_value("TEST",42.0) + fix.db.get_item("TEST").fail = False + widget.setDbkey("TEST") qtbot.addWidget(widget) - widget.setValue(42.0) assert widget.getValue() == 42.0 - widget.setValue(-3.14) - assert widget.getValue() == -3.14 + fix.db.set_value("TEST",3.14) + assert widget.getValue() == 3.14 -@mock.patch("pyefis.instruments.misc.fix") -def test_value_display_flags(mock_fix, qtbot): - mock_item = mock.Mock() - mock_item.value = 100.0 - mock_fix.db.get_item.return_value = mock_item - +#@mock.patch("pyefis.instruments.misc.fix") +#def test_value_display_flags(mock_fix, qtbot): +def test_value_display_flags( qtbot): + #mock_item = mock.Mock() + #mock_item.value = 100.0 + #mock_fix.db.get_item.return_value = mock_item widget = ValueDisplay() + + fix.db.set_value("TEST",100) + widget.setDbkey("TEST") + qtbot.addWidget(widget) widget.failFlag(True) From c5f0f7787c7c58e3fed01c15cb87263e2fa44ddb Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Tue, 11 Jun 2024 20:36:11 -0400 Subject: [PATCH 28/60] Adding framework to automate screenshots of each widget --- conftest.py | 8 +- {mock_db => tests/mock_db}/__init__.py | 0 {mock_db => tests/mock_db}/client.py | 0 {mock_db => tests/mock_db}/scheduler.py | 0 tests/screen/__init__.py | 0 tests/screen/generate_screenshots.py | 77 +++++++++++ tests/screen/gui.py | 169 ++++++++++++++++++++++++ 7 files changed, 250 insertions(+), 4 deletions(-) rename {mock_db => tests/mock_db}/__init__.py (100%) rename {mock_db => tests/mock_db}/client.py (100%) rename {mock_db => tests/mock_db}/scheduler.py (100%) create mode 100644 tests/screen/__init__.py create mode 100644 tests/screen/generate_screenshots.py create mode 100644 tests/screen/gui.py diff --git a/conftest.py b/conftest.py index 5f969b23..ed2f458d 100644 --- a/conftest.py +++ b/conftest.py @@ -1,9 +1,9 @@ # Patch pyavtools.fix so it can run in memory without a fix server import sys -import mock_db.client -import mock_db.scheduler -sys.modules["pyavtools.fix.client"] = mock_db.client -sys.modules["pyavtools.scheduler"] = mock_db.scheduler +import tests.mock_db.client +import tests.mock_db.scheduler +sys.modules["pyavtools.fix.client"] = tests.mock_db.client +sys.modules["pyavtools.scheduler"] = tests.mock_db.scheduler # import fix import pyavtools.fix as fix diff --git a/mock_db/__init__.py b/tests/mock_db/__init__.py similarity index 100% rename from mock_db/__init__.py rename to tests/mock_db/__init__.py diff --git a/mock_db/client.py b/tests/mock_db/client.py similarity index 100% rename from mock_db/client.py rename to tests/mock_db/client.py diff --git a/mock_db/scheduler.py b/tests/mock_db/scheduler.py similarity index 100% rename from mock_db/scheduler.py rename to tests/mock_db/scheduler.py diff --git a/tests/screen/__init__.py b/tests/screen/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/screen/generate_screenshots.py b/tests/screen/generate_screenshots.py new file mode 100644 index 00000000..198c371b --- /dev/null +++ b/tests/screen/generate_screenshots.py @@ -0,0 +1,77 @@ +import pytest +import warnings +import importlib +from unittest import mock +from PyQt5.QtWidgets import QApplication +from PyQt5.QtCore import Qt, QObject +from PyQt5.QtGui import QColor +#from pyefis.screens import screenbuilder +from tests.screen import gui + +class Screen(QObject): + def __init__(self, name, module, config): + super(Screen, self).__init__() + self.name = name + self.module = importlib.import_module(module) + self.config = config + self.object = None + self.default = False + def show(self): + self.object.show() + self.screenShow.emit() + def hide(self): + self.object.hide() + self.screenHide.emit() + + +@pytest.fixture +def app(qtbot): + test_app = QApplication.instance() + if test_app is None: + test_app = QApplication([]) + return test_app + + +def test_save_screenshot(qtbot, request): + + config = { + "main": { + "nodeID": 1, + "screenWidth": 1024, + "screenHeight": 1024, + "screenColor": "0,0,0" + }, + "screens": { + "TEST": { + "layout": { + "rows": 100, + "columns": 100, + }, + "instruments": [ + { + "type": "value_text", + "row": 0, + "column": 0, + "span": { + "rows": 10, + "columns": 10 + }, + "options": { + "dbkey": "TEST" + } + + + } + ] + } + } + } + + #widget = Screen("TEST", "pyefis.screens.screenbuilder", config) + gui.initialize(config,"tests",{}) + qtbot.waitExposed(gui.mainWindow.scr.module) + qtbot.wait(5000) +#show() +#resize(1024,1024) + +#screenbuilder.Screen(config=config) diff --git a/tests/screen/gui.py b/tests/screen/gui.py new file mode 100644 index 00000000..5f53a578 --- /dev/null +++ b/tests/screen/gui.py @@ -0,0 +1,169 @@ +# Copyright (c) 2016 Phil Birkelbach +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +from PyQt5.QtGui import * +from PyQt5.QtCore import * +from PyQt5.QtWidgets import * + +import time +import importlib +import logging +import sys +import os +from pyefis import hooks +from pyefis import hmi +import pyavtools.fix as fix +import pyavtools.scheduler as scheduler + + +# This class is just a structure to hold information about a single +# screen that will be loaded. +class Screen(QObject): + screenShow = pyqtSignal() + screenHide = pyqtSignal() + + def __init__(self, name, module, config): + super(Screen, self).__init__() + self.name = name + self.module = importlib.import_module(module) + self.config = config + + # This would hold the instantiated Screen object from the module. + self.object = None + self.default = False + + def show(self): + self.object.show() + self.screenShow.emit() + + def hide(self): + self.object.hide() + self.screenHide.emit() + + +class Main(QMainWindow): + keyPress = pyqtSignal(QEvent) + keyRelease = pyqtSignal(QEvent) + windowShow = pyqtSignal(QEvent) + windowClose = pyqtSignal(QEvent) + #change_asd_mode = pyqtSignal(QEvent) + def __init__(self, config, config_path, preferences, scr, parent=None): + super(Main, self).__init__(parent) + self.preferences = preferences + self.config_path = config_path + self.scr = scr + self.screenWidth = int(config["main"].get("screenWidth", False)) + self.screenHeight = int(config["main"].get("screenHeight", False)) + if not ( self.screenWidth and self.screenHeight ): + # screenWidth and screenHeight are not defined in the config file + # go full screen + pscreen = QApplication.primaryScreen() + screensize = pscreen.size() + self.screenWidth = screensize.width() + self.screenHeight = screensize.height() + + self.screenColor = config["main"]["screenColor"] + self.nodeID = config["main"].get('nodeID', 1) + + self.setObjectName("EFIS") + self.resize(self.screenWidth, self.screenHeight) + w = QWidget(self) + w.setGeometry(0, 0, self.screenWidth, self.screenHeight) + + p = w.palette() + if self.screenColor: + p.setColor(w.backgroundRole(), QColor(self.screenColor)) + w.setPalette(p) + w.setAutoFillBackground(True) + + print(self.scr) + print(dir(self.scr)) + scr.object = self.scr.module.Screen(self) + setattr(scr.object,'screenName',scr.name) + scr.object.resize(self.width(), self.height()) + scr.object.move(0,0) + scr.show() + + + #def doExit(self, s=""): + # Ensure external processes are terminated before exiting + # For example waydroid/weston if they are in use + #for s in screens: + # s.object.close() + # Close down fix connections + # This needs done before the main event loop is stopped below + + # We send signals for these events so everybody can play. + def showEvent(self, event): + self.windowShow.emit(event) + + def closeEvent(self, event): + log.debug("Window Close event received") + self.windowClose.emit(event) + + def keyPressEvent(self, event): + self.keyPress.emit(event) + + def keyReleaseEvent(self, event): + self.keyRelease.emit(event) + + # def change_asd_mode_event (self, event): + # self.change_asd_mode.emit(event) + + def get_config_item(self, child, key): + print(f"child:{child,} key:{key}") + if self.scr.object == child: + print("match") + print(self.scr.config) + return self.scr.config.get(key) + else: + return None + + +def initialize(config,config_path,preferences): + global mainWindow + global log + log = logging.getLogger(__name__) + log.info("Initializing Graphics") + module = "pyefis.screens.screenbuilder" + + scr = Screen("TEST", module, config['screens']["TEST"]) + print(scr) + mainWindow = Main(config,config_path,preferences,scr) + + mainWindow.showFullScreen() + + def button_timeout(): + # set MENUTIMEOUT True + button_key.value = True + + def button_timeout_reset(): + if not button_key.value: + #When set to False reset timer + button_timer.start() + + if config['main'].get('button_timeout', False): + scheduler.initialize() + button_timer = scheduler.scheduler.getTimer(config['main']['button_timeout']) + if not button_timer: + scheduler.scheduler.timers.append(scheduler.IntervalTimer(config['main']['button_timeout'])) + scheduler.scheduler.timers[-1].start() + button_timer = scheduler.scheduler.getTimer(config['main']['button_timeout']) + button_key = fix.db.get_item('HIDEBUTTON') + button_timer.add_callback(button_timeout) + button_key.valueWrite[bool].connect(button_timeout_reset) + + From 6477d5ffc59236efe86ee630723456e0e626bae9 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Wed, 12 Jun 2024 16:19:30 -0400 Subject: [PATCH 29/60] Progress on screenshots --- tests/{screen => screenshots}/__init__.py | 0 tests/{screen => screenshots}/generate_screenshots.py | 0 tests/{screen => screenshots}/gui.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename tests/{screen => screenshots}/__init__.py (100%) rename tests/{screen => screenshots}/generate_screenshots.py (100%) rename tests/{screen => screenshots}/gui.py (100%) diff --git a/tests/screen/__init__.py b/tests/screenshots/__init__.py similarity index 100% rename from tests/screen/__init__.py rename to tests/screenshots/__init__.py diff --git a/tests/screen/generate_screenshots.py b/tests/screenshots/generate_screenshots.py similarity index 100% rename from tests/screen/generate_screenshots.py rename to tests/screenshots/generate_screenshots.py diff --git a/tests/screen/gui.py b/tests/screenshots/gui.py similarity index 100% rename from tests/screen/gui.py rename to tests/screenshots/gui.py From 465b852884df9129fef9c7dcaa436de238c6b03f Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Wed, 12 Jun 2024 16:21:48 -0400 Subject: [PATCH 30/60] Added some code so we can use screenbuilder to create the displays for the screenshots --- conftest.py | 62 ++++++++++++++++- ...fis.instruments.gauges.NumericDisplay.yaml | 68 +++++++++++++++++++ tests/screenshots/generate_screenshots.py | 55 ++------------- tests/screenshots/gui.py | 3 - 4 files changed, 133 insertions(+), 55 deletions(-) create mode 100644 tests/screenshots/configs/pyefis.instruments.gauges.NumericDisplay.yaml diff --git a/conftest.py b/conftest.py index ed2f458d..d63b1ead 100644 --- a/conftest.py +++ b/conftest.py @@ -16,8 +16,68 @@ # key,desc, dtype(as a string), min, max, units, tol, aux fix.db.define_item("TEST", "Testing key", 'float', 0, 100, '', 50000, "") # Set initial Value -fix.db.set_value("TEST", 50) +fix.db.set_value("TEST", 50.19) # Bad and fail are true by default to set them False fix.db.get_item("TEST").bad = False fix.db.get_item("TEST").fail = False + +fix.db.define_item("NUMOK", "Number OK", 'float', 0, 100, 'degC', 50000, "") +fix.db.set_value("NUMOK", 50.19) +fix.db.get_item("NUMOK").bad = False +fix.db.get_item("NUMOK").fail = False + +fix.db.define_item("NUMOLD", "Number OK", 'float', 0, 100, 'degC', 5, "") +fix.db.set_value("NUMOLD", 50.19) +fix.db.get_item("NUMOLD").old = True +fix.db.get_item("NUMOLD").bad = False +fix.db.get_item("NUMOLD").fail = False + +fix.db.define_item("NUMBAD", "Number OK", 'float', 0, 100, 'degC', 50000, "") +fix.db.set_value("NUMBAD", 50.19) +fix.db.get_item("NUMBAD").old = False +fix.db.get_item("NUMBAD").bad = True +fix.db.get_item("NUMBAD").fail = False + +fix.db.define_item("NUMFAIL", "Number OK", 'float', 0, 100, 'degC', 50000, "") +fix.db.set_value("NUMFAIL", 50.19) +fix.db.get_item("NUMFAIL").old = False +fix.db.get_item("NUMFAIL").bad = False +fix.db.get_item("NUMFAIL").fail = True + +fix.db.define_item("NUMLOWALARM", "Number OK", 'float', 0, 100, 'degC', 50000, "Min,Max,highWarn,highAlarm,lowWarn,lowAlarm") +fix.db.set_value("NUMLOWALARM", 50.19) +fix.db.get_item("NUMLOWALARM").old = False +fix.db.get_item("NUMLOWALARM").bad = False +fix.db.get_item("NUMLOWALARM").fail = False +fix.db.get_item("NUMLOWALARM").set_aux_value("Min",40) +fix.db.get_item("NUMLOWALARM").set_aux_value("lowAlarm",60) +fix.db.get_item("NUMLOWALARM").set_aux_value("lowWarn",70) +fix.db.get_item("NUMLOWALARM").set_aux_value("highWarn",80) +fix.db.get_item("NUMLOWALARM").set_aux_value("highAlarm",90) +fix.db.get_item("NUMLOWALARM").set_aux_value("Max",100) + +fix.db.define_item("NUMLOWWARN", "Number OK", 'float', 0, 100, 'degC', 50000, "Min,Max,highWarn,highAlarm,lowWarn,lowAlarm") +fix.db.set_value("NUMLOWWARN", 50.19) +fix.db.get_item("NUMLOWWARN").old = False +fix.db.get_item("NUMLOWWARN").bad = False +fix.db.get_item("NUMLOWWARN").fail = False +fix.db.get_item("NUMLOWWARN").set_aux_value("Min",40) +fix.db.get_item("NUMLOWWARN").set_aux_value("lowAlarm",45) +fix.db.get_item("NUMLOWWARN").set_aux_value("lowWarn",55) +fix.db.get_item("NUMLOWWARN").set_aux_value("highWarn",80) +fix.db.get_item("NUMLOWWARN").set_aux_value("highAlarm",90) +fix.db.get_item("NUMLOWWARN").set_aux_value("Max",100) + + +fix.db.define_item("NUMMAX", "Number OK", 'float', 0, 100, 'degC', 50000, "Min,Max,highWarn,highAlarm,lowWarn,lowAlarm") +fix.db.set_value("NUMMAX", 50.19) +fix.db.get_item("NUMMAX").old = False +fix.db.get_item("NUMMAX").bad = False +fix.db.get_item("NUMMAX").fail = False +fix.db.get_item("NUMMAX").set_aux_value("Max",40) + + + + + diff --git a/tests/screenshots/configs/pyefis.instruments.gauges.NumericDisplay.yaml b/tests/screenshots/configs/pyefis.instruments.gauges.NumericDisplay.yaml new file mode 100644 index 00000000..8c99bc85 --- /dev/null +++ b/tests/screenshots/configs/pyefis.instruments.gauges.NumericDisplay.yaml @@ -0,0 +1,68 @@ +main: + nodeID: 1 + screenWidth: 1024 + screenHeight: 1024 + screenFullSize: True + screenColor: (0,0,0) + +screens: + TEST: + layout: + rows: 100 + columns: 100 + draw_grid: true + instruments: + - type: ganged_numeric_display + gang_type: vertical + row: 10 + column: 10 + span: + rows: 90 + columns: 30 + groups: + - name: ROW1 + gap: 0 + common_options: + dbkey: NUMOK + decimal_places: 2 + show_units: false + instruments: + - + options: + show_units: false + - + options: + font_mask: "00.00" + - + options: + font_mask: "00.00" + font_family: "DSEG14 Classic" + #units_font_mask: '~~' + + - + options: + font_mask: "00.00" + font_family: "DSEG14 Classic" + #units_font_mask: '~~' + font_ghost_mask: "~~.~~" + - + options: + font_mask: "00.00" + dbkey: NUMOLD + - + options: + font_mask: "00.00" + dbkey: NUMBAD + - + options: + font_mask: "00.00" + dbkey: NUMFAIL + - + options: + font_mask: "00.00" + dbkey: NUMLOWWARN + - + options: + font_mask: "00.00" + dbkey: NUMLOWALARM + diff --git a/tests/screenshots/generate_screenshots.py b/tests/screenshots/generate_screenshots.py index 198c371b..63bbf89d 100644 --- a/tests/screenshots/generate_screenshots.py +++ b/tests/screenshots/generate_screenshots.py @@ -6,23 +6,8 @@ from PyQt5.QtCore import Qt, QObject from PyQt5.QtGui import QColor #from pyefis.screens import screenbuilder -from tests.screen import gui - -class Screen(QObject): - def __init__(self, name, module, config): - super(Screen, self).__init__() - self.name = name - self.module = importlib.import_module(module) - self.config = config - self.object = None - self.default = False - def show(self): - self.object.show() - self.screenShow.emit() - def hide(self): - self.object.hide() - self.screenHide.emit() - +from tests.screenshots import gui +import yaml @pytest.fixture def app(qtbot): @@ -33,40 +18,8 @@ def app(qtbot): def test_save_screenshot(qtbot, request): - - config = { - "main": { - "nodeID": 1, - "screenWidth": 1024, - "screenHeight": 1024, - "screenColor": "0,0,0" - }, - "screens": { - "TEST": { - "layout": { - "rows": 100, - "columns": 100, - }, - "instruments": [ - { - "type": "value_text", - "row": 0, - "column": 0, - "span": { - "rows": 10, - "columns": 10 - }, - "options": { - "dbkey": "TEST" - } - - - } - ] - } - } - } - + with open("tests/screenshots/configs/pyefis.instruments.gauges.NumericDisplay.yaml") as cf: + config = yaml.safe_load(cf) #widget = Screen("TEST", "pyefis.screens.screenbuilder", config) gui.initialize(config,"tests",{}) qtbot.waitExposed(gui.mainWindow.scr.module) diff --git a/tests/screenshots/gui.py b/tests/screenshots/gui.py index 5f53a578..b0052b04 100644 --- a/tests/screenshots/gui.py +++ b/tests/screenshots/gui.py @@ -89,10 +89,7 @@ def __init__(self, config, config_path, preferences, scr, parent=None): w.setPalette(p) w.setAutoFillBackground(True) - print(self.scr) - print(dir(self.scr)) scr.object = self.scr.module.Screen(self) - setattr(scr.object,'screenName',scr.name) scr.object.resize(self.width(), self.height()) scr.object.move(0,0) scr.show() From e0cedb20d067c3aaf795e1353dfb13ac26a9b69c Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Wed, 12 Jun 2024 21:10:44 -0400 Subject: [PATCH 31/60] Updated gauges to return padded number from getValueText if font_mask is provided --- src/pyefis/instruments/gauges/abstract.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pyefis/instruments/gauges/abstract.py b/src/pyefis/instruments/gauges/abstract.py index 3b298794..12d28eaa 100644 --- a/src/pyefis/instruments/gauges/abstract.py +++ b/src/pyefis/instruments/gauges/abstract.py @@ -179,7 +179,10 @@ def getValueText(self): return '{0:.{1}f}'.format(float(self.encoder_set_value), self.decimal_places) else: # return properly formatted value - return '{0:.{1}f}'.format(float(self.value), self.decimal_places) + if self.font_mask != None: + return '{0:{1}.{2}f}'.format(float(self.value),len(self.font_mask), self.decimal_places) + else: + return '{0:.{1}f}'.format(float(self.value), self.decimal_places) valueText = property(getValueText) From beac3c0c410bc6b4704337bbad7e8b0b6e747258 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Wed, 12 Jun 2024 21:11:36 -0400 Subject: [PATCH 32/60] Added missing units ghost font to numerical_display --- src/pyefis/instruments/gauges/numeric.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/pyefis/instruments/gauges/numeric.py b/src/pyefis/instruments/gauges/numeric.py index f7e26af8..9d630cc5 100644 --- a/src/pyefis/instruments/gauges/numeric.py +++ b/src/pyefis/instruments/gauges/numeric.py @@ -82,4 +82,13 @@ def paintEvent(self, event): if self.show_units: p.setFont(self.smallFont) opt = QTextOption(self.unitsAlignment) + if self.units_font_ghost_mask: + alpha = self.textColor.alpha() + self.textColor.setAlpha(self.font_ghost_alpha) + pen.setColor(self.textColor) + p.setPen(pen) + p.drawText(self.unitsTextRect, self.units_font_ghost_mask , opt) + self.textColor.setAlpha(alpha) + pen.setColor(self.textColor) + p.setPen(pen) p.drawText(self.unitsTextRect, self.units, opt) From 2e3c5df02702b5495cab2d57b28e4cce453dce67 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Wed, 12 Jun 2024 21:12:53 -0400 Subject: [PATCH 33/60] first screenshot capture setup and working --- ...fis.instruments.gauges.NumericDisplay.yaml | 125 +++++++++++++++++- tests/screenshots/generate_screenshots.py | 17 ++- 2 files changed, 132 insertions(+), 10 deletions(-) diff --git a/tests/screenshots/configs/pyefis.instruments.gauges.NumericDisplay.yaml b/tests/screenshots/configs/pyefis.instruments.gauges.NumericDisplay.yaml index 8c99bc85..db4aef1f 100644 --- a/tests/screenshots/configs/pyefis.instruments.gauges.NumericDisplay.yaml +++ b/tests/screenshots/configs/pyefis.instruments.gauges.NumericDisplay.yaml @@ -1,7 +1,7 @@ main: nodeID: 1 - screenWidth: 1024 - screenHeight: 1024 + screenWidth: 1000 + screenHeight: 1000 screenFullSize: True screenColor: (0,0,0) @@ -26,6 +26,7 @@ screens: dbkey: NUMOK decimal_places: 2 show_units: false + font_family: DejaVu Sans Mono instruments: - options: @@ -36,15 +37,129 @@ screens: - options: font_mask: "00.00" - font_family: "DSEG14 Classic" + font_family: "DSEG14 Classic" #units_font_mask: '~~' + font_ghost_mask: "~~.~~" + #units_font_ghost_mask: "~~" + - + options: + font_mask: "00.00" + dbkey: NUMOLD + - + options: + font_mask: "00.00" + dbkey: NUMBAD + - + options: + font_mask: "00.00" + dbkey: NUMFAIL + - + options: + font_mask: "00.00" + dbkey: NUMANNUNCIATE + - + options: + font_mask: "00.00" + dbkey: NUMLOWWARN + - + options: + font_mask: "00.00" + dbkey: NUMLOWALARM + - type: ganged_numeric_display + gang_type: vertical + row: 10 + column: 45 + span: + rows: 90 + columns: 30 + groups: + - name: ROW1 + gap: 0 + common_options: + dbkey: NUMOK + decimal_places: 2 + show_units: true + font_family: DejaVu Sans Mono + instruments: + - + options: + show_units: false + - + options: + font_mask: "00.00" + - + options: + font_mask: "00.00" + font_family: "DSEG14 Classic" + units_font_mask: '~~' + font_ghost_mask: "~~.~~" + units_font_ghost_mask: "~~" + - + options: + font_mask: "00.00" + dbkey: NUMOLD + units_font_mask: '~~' + - + options: + font_mask: "00.00" + dbkey: NUMBAD + units_font_mask: '~~' + - + options: + font_mask: "00.00" + dbkey: NUMFAIL + units_font_mask: '~~' + - + options: + font_mask: "00.00" + dbkey: NUMANNUNCIATE + - + options: + font_mask: "00.00" + dbkey: NUMLOWWARN + units_font_mask: '~~' + - + options: + font_mask: "00.00" + dbkey: NUMLOWALARM + units_font_mask: '~~' + + + + + + + - type: ganged_numeric_display + gang_type: vertical + row: 10 + column: 80 + span: + rows: 90 + columns: 20 + groups: + - name: ROW1 + gap: 0 + common_options: + dbkey: NUMOK + decimal_places: 2 + show_units: false + font_family: DejaVu Sans Mono + + instruments: + - + options: + show_units: false + - + options: + font_mask: "00.00" - options: font_mask: "00.00" font_family: "DSEG14 Classic" #units_font_mask: '~~' font_ghost_mask: "~~.~~" + #units_font_ghost_mask: "~~" - options: font_mask: "00.00" @@ -57,6 +172,10 @@ screens: options: font_mask: "00.00" dbkey: NUMFAIL + - + options: + font_mask: "00.00" + dbkey: NUMANNUNCIATE - options: font_mask: "00.00" diff --git a/tests/screenshots/generate_screenshots.py b/tests/screenshots/generate_screenshots.py index 63bbf89d..587ec947 100644 --- a/tests/screenshots/generate_screenshots.py +++ b/tests/screenshots/generate_screenshots.py @@ -5,9 +5,9 @@ from PyQt5.QtWidgets import QApplication from PyQt5.QtCore import Qt, QObject from PyQt5.QtGui import QColor -#from pyefis.screens import screenbuilder from tests.screenshots import gui import yaml +import os @pytest.fixture def app(qtbot): @@ -17,14 +17,17 @@ def app(qtbot): return test_app -def test_save_screenshot(qtbot, request): +def test_save_screenshot_numeric_display(qtbot, request): with open("tests/screenshots/configs/pyefis.instruments.gauges.NumericDisplay.yaml") as cf: config = yaml.safe_load(cf) #widget = Screen("TEST", "pyefis.screens.screenbuilder", config) gui.initialize(config,"tests",{}) qtbot.waitExposed(gui.mainWindow.scr.module) - qtbot.wait(5000) -#show() -#resize(1024,1024) - -#screenbuilder.Screen(config=config) + path = qtbot.screenshot(gui.mainWindow, "numeric_display") + qtbot.wait(500) + os.rename( + path, + request.config.rootdir + + "/extras/extras/test_results/numeric_display.png", + ) + qtbot.wait(10000) From 3e78cfc4ccd067996ef7999d02c251ec22dd39b3 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Wed, 12 Jun 2024 21:17:40 -0400 Subject: [PATCH 34/60] Updated fit_to_mask so it remdered closer to the largest size possible as expected --- src/pyefis/instruments/helpers/__init__.py | 62 ++++++++++++---------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/src/pyefis/instruments/helpers/__init__.py b/src/pyefis/instruments/helpers/__init__.py index d11e841a..c4eae8d4 100644 --- a/src/pyefis/instruments/helpers/__init__.py +++ b/src/pyefis/instruments/helpers/__init__.py @@ -1,56 +1,64 @@ from PyQt5.QtWidgets import * from PyQt5.QtGui import * -def fit_to_mask(width,height,mask,font,units_mask=None, units_ratio=0.8): +def fit_to_mask(iwidth,iheight,mask,font,units_mask=None, units_ratio=0.8): + # Could not get font metrics to work perfectly + # Seems like text it rendered starting slightly to the right + # rsulting in the right side of the text getting cut off + # Fitting to a slightly smaller width and height + # seems to resolve the issue. + width = iwidth * 0.95 + height = iheight * 0.96 font_size = height text_font = QFont(font, int(font_size)) units_font = QFont(font, int(font_size * units_ratio)) - t = QGraphicsSimpleTextItem(mask) - t.setFont(text_font) - text_width = t.boundingRect().width() - text_height = t.boundingRect().height() + fm = QFontMetrics(text_font) + text_width = fm.tightBoundingRect(mask).width() + text_height = fm.tightBoundingRect(mask).height() units_width = 0 units_height = 0 if units_mask: - u = QGraphicsSimpleTextItem(units_mask) - u.setFont(units_font) - units_width = u.boundingRect().width() - units_height = u.boundingRect().height() + fmu = QFontMetrics(units_font) + units_width = fmu.tightBoundingRect(units_mask).width() + units_height = fmu.tightBoundingRect(units_mask).height() # IF needed, shrink until it fits - while (text_width + units_width >= width) and font_size > 0.5: + while ( (text_width + units_width > width) or text_height > height ) and font_size > 0.5: font_size -= 0.2 + print(f"too wide trying: {font_size} text_width:{text_width}, units_width:{units_width}, width:{width}") text_font.setPointSizeF(font_size) - t.setFont(text_font) - text_width = t.boundingRect().width() - text_height = t.boundingRect().height() + fm = QFontMetrics(text_font) + text_width = fm.tightBoundingRect(mask).width() + text_height = fm.tightBoundingRect(mask).height() if units_mask: units_font.setPointSizeF(font_size * units_ratio) - u.setFont(units_font) - units_width = u.boundingRect().width() - units_height = u.boundingRect().height() - + fmu = QFontMetrics(units_font) + units_width = fmu.tightBoundingRect(units_mask).width() + units_height = fmu.tightBoundingRect(units_mask).height() # If needed, grow until it fills - while text_width + units_width <= width: + while (text_width + units_width < width) and text_height < height: font_size += 0.2 + print(f"Not wide enough, trying {font_size} text_width:{text_width}, units_width:{units_width}, width:{width}") text_font.setPointSizeF(font_size) - t.setFont(text_font) - text_width = t.boundingRect().width() - text_height = t.boundingRect().height() + fm = QFontMetrics(text_font) + text_width = fm.tightBoundingRect(mask).width() + text_height = fm.tightBoundingRect(mask).height() if units_mask: units_font.setPointSizeF(font_size * units_ratio) - u.setFont(units_font) - units_width = u.boundingRect().width() - units_height = u.boundingRect().height() + fmu = QFontMetrics(units_font) + units_width = fmu.tightBoundingRect(units_mask).width() + units_height = fmu.tightBoundingRect(units_mask).height() + + font_size -= 0.2 # The above took care of the width, this addresses the height: while (text_height >= height) and font_size > 0.5: font_size -= 0.2 + print(f"too high, trying {font_size} text_height:{text_height}, height:{height}") text_font.setPointSizeF(font_size) - t.setFont(text_font) - text_width = t.boundingRect().width() - text_height = t.boundingRect().height() + fm = QFontMetrics(text_font) + text_width = fm.tightBoundingRect(mask).width() return(font_size) From 617810bd47a86f523ab5d0bb473dbcad7805d851 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Wed, 12 Jun 2024 21:35:48 -0400 Subject: [PATCH 35/60] Added missing unit_font_mask to numerical_display screenshot --- conftest.py | 77 ++++++------------- ...fis.instruments.gauges.NumericDisplay.yaml | 2 + 2 files changed, 25 insertions(+), 54 deletions(-) diff --git a/conftest.py b/conftest.py index d63b1ead..9f688b6e 100644 --- a/conftest.py +++ b/conftest.py @@ -22,60 +22,29 @@ fix.db.get_item("TEST").fail = False -fix.db.define_item("NUMOK", "Number OK", 'float', 0, 100, 'degC', 50000, "") -fix.db.set_value("NUMOK", 50.19) -fix.db.get_item("NUMOK").bad = False -fix.db.get_item("NUMOK").fail = False - -fix.db.define_item("NUMOLD", "Number OK", 'float', 0, 100, 'degC', 5, "") -fix.db.set_value("NUMOLD", 50.19) -fix.db.get_item("NUMOLD").old = True -fix.db.get_item("NUMOLD").bad = False -fix.db.get_item("NUMOLD").fail = False - -fix.db.define_item("NUMBAD", "Number OK", 'float', 0, 100, 'degC', 50000, "") -fix.db.set_value("NUMBAD", 50.19) -fix.db.get_item("NUMBAD").old = False -fix.db.get_item("NUMBAD").bad = True -fix.db.get_item("NUMBAD").fail = False - -fix.db.define_item("NUMFAIL", "Number OK", 'float', 0, 100, 'degC', 50000, "") -fix.db.set_value("NUMFAIL", 50.19) -fix.db.get_item("NUMFAIL").old = False -fix.db.get_item("NUMFAIL").bad = False -fix.db.get_item("NUMFAIL").fail = True - -fix.db.define_item("NUMLOWALARM", "Number OK", 'float', 0, 100, 'degC', 50000, "Min,Max,highWarn,highAlarm,lowWarn,lowAlarm") -fix.db.set_value("NUMLOWALARM", 50.19) -fix.db.get_item("NUMLOWALARM").old = False -fix.db.get_item("NUMLOWALARM").bad = False -fix.db.get_item("NUMLOWALARM").fail = False -fix.db.get_item("NUMLOWALARM").set_aux_value("Min",40) -fix.db.get_item("NUMLOWALARM").set_aux_value("lowAlarm",60) -fix.db.get_item("NUMLOWALARM").set_aux_value("lowWarn",70) -fix.db.get_item("NUMLOWALARM").set_aux_value("highWarn",80) -fix.db.get_item("NUMLOWALARM").set_aux_value("highAlarm",90) -fix.db.get_item("NUMLOWALARM").set_aux_value("Max",100) - -fix.db.define_item("NUMLOWWARN", "Number OK", 'float', 0, 100, 'degC', 50000, "Min,Max,highWarn,highAlarm,lowWarn,lowAlarm") -fix.db.set_value("NUMLOWWARN", 50.19) -fix.db.get_item("NUMLOWWARN").old = False -fix.db.get_item("NUMLOWWARN").bad = False -fix.db.get_item("NUMLOWWARN").fail = False -fix.db.get_item("NUMLOWWARN").set_aux_value("Min",40) -fix.db.get_item("NUMLOWWARN").set_aux_value("lowAlarm",45) -fix.db.get_item("NUMLOWWARN").set_aux_value("lowWarn",55) -fix.db.get_item("NUMLOWWARN").set_aux_value("highWarn",80) -fix.db.get_item("NUMLOWWARN").set_aux_value("highAlarm",90) -fix.db.get_item("NUMLOWWARN").set_aux_value("Max",100) - - -fix.db.define_item("NUMMAX", "Number OK", 'float', 0, 100, 'degC', 50000, "Min,Max,highWarn,highAlarm,lowWarn,lowAlarm") -fix.db.set_value("NUMMAX", 50.19) -fix.db.get_item("NUMMAX").old = False -fix.db.get_item("NUMMAX").bad = False -fix.db.get_item("NUMMAX").fail = False -fix.db.get_item("NUMMAX").set_aux_value("Max",40) +def create_numbers(key,value,old=False,bad=False,fail=False,annunciate=False): + fix.db.define_item(key, "Number", 'float', 0, 100, 'degC', 50000, "Min,Max,highWarn,highAlarm,lowWarn,lowAlarm") + fix.db.set_value(key, value) + fix.db.get_item(key).old = old + fix.db.get_item(key).bad = bad + fix.db.get_item(key).fail = fail + fix.db.get_item(key).annunciate = annunciate + fix.db.get_item(key).set_aux_value("Min",0) + fix.db.get_item(key).set_aux_value("lowAlarm",10) + fix.db.get_item(key).set_aux_value("lowWarn",20) + fix.db.get_item(key).set_aux_value("highWarn",80) + fix.db.get_item(key).set_aux_value("highAlarm",90) + fix.db.get_item(key).set_aux_value("Max",100) + +create_numbers("NUMOK", 50.91) +create_numbers("NUMOLD", 51.82, old=True) +create_numbers("NUMBAD", 48.73, bad=True) +create_numbers("NUMFAIL", 65.64, fail=True) +create_numbers("NUMANNUNCIATE", 62.55, annunciate=True) +create_numbers("NUMLOWWARN",18.46) +create_numbers("NUMLOWALARM", 5.37) +create_numbers("NUMHIGHWARN", 81.28) +create_numbers("NUMHIGHALARM", 95.19) diff --git a/tests/screenshots/configs/pyefis.instruments.gauges.NumericDisplay.yaml b/tests/screenshots/configs/pyefis.instruments.gauges.NumericDisplay.yaml index db4aef1f..f5f3876d 100644 --- a/tests/screenshots/configs/pyefis.instruments.gauges.NumericDisplay.yaml +++ b/tests/screenshots/configs/pyefis.instruments.gauges.NumericDisplay.yaml @@ -88,6 +88,7 @@ screens: - options: font_mask: "00.00" + units_font_mask: '~~' - options: font_mask: "00.00" @@ -114,6 +115,7 @@ screens: options: font_mask: "00.00" dbkey: NUMANNUNCIATE + units_font_mask: '~~' - options: font_mask: "00.00" From 0127f1ec9e52666a65b93697e86f0be4af19a9c3 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Wed, 12 Jun 2024 21:36:13 -0400 Subject: [PATCH 36/60] Adjusted position of units in numberical_display --- src/pyefis/instruments/gauges/numeric.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pyefis/instruments/gauges/numeric.py b/src/pyefis/instruments/gauges/numeric.py index 9d630cc5..67143aa4 100644 --- a/src/pyefis/instruments/gauges/numeric.py +++ b/src/pyefis/instruments/gauges/numeric.py @@ -50,7 +50,7 @@ def resizeEvent(self, event): # TODO Edit to get the units closer to the value self.valueTextRect = QRectF(0, 0, self.width()-unitsWidth-5, self.height()) self.unitsTextRect = QRectF(self.valueTextRect.width(), 0, - self.width()-self.valueTextRect.width(), self.height()) + self.width()-self.valueTextRect.width()-2, self.height() * self.small_font_percent) else: self.valueTextRect = QRectF(0, 0, self.width(), self.height()) From 71e6c0f6e679f49b0303b909b9ca1cb7d9e04b08 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Wed, 12 Jun 2024 21:37:20 -0400 Subject: [PATCH 37/60] Updated gauges to return xxx on fail in same number of characters as font_mask if font_mask is provided --- src/pyefis/instruments/gauges/abstract.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pyefis/instruments/gauges/abstract.py b/src/pyefis/instruments/gauges/abstract.py index 12d28eaa..ef43826b 100644 --- a/src/pyefis/instruments/gauges/abstract.py +++ b/src/pyefis/instruments/gauges/abstract.py @@ -158,7 +158,10 @@ def setValue(self, value): def getValueText(self): if self.fail: - return 'xxx' + if self.font_mask: + return ''.join('X' if x != '.' else x for x in self.font_mask) + else: + return 'xxx' else: if self.encoder_selected and self.encoder_num_mask: if self.encoder_num_blink: From 6274fd1cb3991a99b7d7afc7b69290d87265a486 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Thu, 13 Jun 2024 20:16:32 -0400 Subject: [PATCH 38/60] Added tests for instruments.NumericalDiplay --- tests/instruments/test_numerical_display.py | 80 +++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 tests/instruments/test_numerical_display.py diff --git a/tests/instruments/test_numerical_display.py b/tests/instruments/test_numerical_display.py new file mode 100644 index 00000000..491a643f --- /dev/null +++ b/tests/instruments/test_numerical_display.py @@ -0,0 +1,80 @@ +import pytest +from unittest import mock +from PyQt5.QtWidgets import QApplication +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QColor, QBrush +from pyefis.instruments.NumericalDisplay import NumericalDisplay +import pyavtools.fix as fix + + +@pytest.fixture +def app(qtbot): + test_app = QApplication.instance() + if test_app is None: + test_app = QApplication([]) + return test_app + + +def test_numerical_display_default(qtbot): + widget = NumericalDisplay(total_decimals=4, scroll_decimal=2) + qtbot.addWidget(widget) + assert widget.total_decimals == 4 + assert widget.scroll_decimal == 2 + assert widget._bad == False + assert widget.value == 0 + widget.show() + qtbot.waitExposed(widget) + assert widget.isVisible() + + +def test_numerical_display_decimals(qtbot): + widget = NumericalDisplay(total_decimals=5, scroll_decimal=2, font_size=20) + qtbot.addWidget(widget) + widget.resize(80, 50) + widget.show() + widget.setValue(val=1234) + qtbot.waitExposed(widget) + assert widget.font_size != 20 + assert widget.pre_scroll_text.text() == "012" + widget.setValue(val=54321) + assert widget.pre_scroll_text.text() == "543" + widget.setBad(True) + widget.setValue(val=43210) + widget.getValue() + assert widget._bad == True + widget.resize(85, 50) + assert widget.pre_scroll_text.text() == "" + assert widget.value == 43210 + assert widget.pre_scroll_text.text() != "432" + a = widget.getValue() + assert a == 43210 + assert widget.pre_scroll_text.text() == "" + assert widget.getBad() == True + widget.setOld(True) + assert widget.getOld() == True + widget.setOld(False) + assert widget.getOld() == False + widget.setFail(True) + assert widget.getFail() == True + widget.setFail(False) + assert widget.getFail() == False + + widget.setBad(False) + widget.setValue(val=54321) + assert widget.pre_scroll_text.brush() == QBrush(QColor(Qt.white)) + + widget.setValue(val=-5432) + assert widget.pre_scroll_text.text() == "-54" + widget.setValue(val=-52) + assert widget.pre_scroll_text.text() == "-00" + + +# Not sure why line 270 is not showing as covered +# when this calls the getValue function: +def test_numerical_get_value(qtbot): + widget = NumericalDisplay(total_decimals=5, scroll_decimal=2, font_size=20) + qtbot.addWidget(widget) + widget.setValue(20) + widget.getValue() + assert widget._value == 20 + assert widget.getValue() == 20 From d10a167e07d9b121a50d490e71e56dabb4312234 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Thu, 13 Jun 2024 21:50:16 -0400 Subject: [PATCH 39/60] Fixed airspeed dial to set initial value properly --- src/pyefis/instruments/airspeed/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pyefis/instruments/airspeed/__init__.py b/src/pyefis/instruments/airspeed/__init__.py index 613ffbc1..5101ea7d 100644 --- a/src/pyefis/instruments/airspeed/__init__.py +++ b/src/pyefis/instruments/airspeed/__init__.py @@ -37,6 +37,7 @@ def __init__(self, parent=None, font_percent=0.07, bg_color=Qt.black, font_famil self.bg_color = bg_color self._airspeed = 0 self.item = fix.db.get_item("IAS") + self._airspeed = self.item.value self.item.valueChanged[float].connect(self.setAirspeed) self.item.oldChanged[bool].connect(self.repaint) self.item.badChanged[bool].connect(self.repaint) From 2a795dc772a09547e83f58b5f6c6fdcd5046d1c0 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Thu, 13 Jun 2024 21:52:13 -0400 Subject: [PATCH 40/60] formatted airspeed and numericalDisplay with black --- .../instruments/NumericalDisplay/__init__.py | 150 +++++----- src/pyefis/instruments/airspeed/__init__.py | 256 +++++++++++------- 2 files changed, 241 insertions(+), 165 deletions(-) diff --git a/src/pyefis/instruments/NumericalDisplay/__init__.py b/src/pyefis/instruments/NumericalDisplay/__init__.py index af190f62..fbd2bf9b 100644 --- a/src/pyefis/instruments/NumericalDisplay/__init__.py +++ b/src/pyefis/instruments/NumericalDisplay/__init__.py @@ -18,8 +18,16 @@ from PyQt5.QtCore import * from PyQt5.QtWidgets import * + class NumericalDisplay(QGraphicsView): - def __init__(self, parent=None, total_decimals=3, scroll_decimal=1, font_family="DejaVu Sans Mono", font_size=15): + def __init__( + self, + parent=None, + total_decimals=3, + scroll_decimal=1, + font_family="DejaVu Sans Mono", + font_size=15, + ): super(NumericalDisplay, self).__init__(parent) self.setStyleSheet("border: 0px") self.font_family = font_family @@ -42,23 +50,23 @@ def resizeEvent(self, event): self.w = self.width() self.h = self.height() - t = QGraphicsSimpleTextItem ("9") - t.setFont (self.f) + t = QGraphicsSimpleTextItem("9") + t.setFont(self.f) font_width = t.boundingRect().width() font_height = t.boundingRect().height() while font_width * (self.total_decimals) >= self.w - 0.1: - self.font_size -= 0.1 - self.f.setPointSizeF(self.font_size) - t.setFont (self.f) - font_width = t.boundingRect().width() - font_height = t.boundingRect().height() + self.font_size -= 0.1 + self.f.setPointSizeF(self.font_size) + t.setFont(self.f) + font_width = t.boundingRect().width() + font_height = t.boundingRect().height() while font_width * (self.total_decimals) <= self.w - 0.1: - self.font_size += 0.1 - self.f.setPointSizeF(self.font_size) - t.setFont (self.f) - font_width = t.boundingRect().width() - font_height = t.boundingRect().height() + self.font_size += 0.1 + self.f.setPointSizeF(self.font_size) + t.setFont(self.f) + font_width = t.boundingRect().width() + font_height = t.boundingRect().height() self.font_size = qRound(self.font_size) self.f = QFont(self.font_family, self.font_size) @@ -66,47 +74,53 @@ def resizeEvent(self, event): border_width = 1 top = (self.h - font_height) / 2 rect_pen = QPen(QColor(Qt.white)) - rect_pen.setWidth (border_width) - self.scene.addRect(0, top, self.w, font_height, - rect_pen, QBrush(QColor(Qt.black))) + rect_pen.setWidth(border_width) + self.scene.addRect( + 0, top, self.w, font_height, rect_pen, QBrush(QColor(Qt.black)) + ) self.setScene(self.scene) - self.scrolling_area = NumericalScrollDisplay(self, self.scroll_decimal, - self.font_family, self.font_size) - self.scene.addWidget (self.scrolling_area) + self.scrolling_area = NumericalScrollDisplay( + self, self.scroll_decimal, self.font_family, self.font_size + ) + self.scene.addWidget(self.scrolling_area) self.digit_vertical_spacing = font_height - self.scrolling_area.resize(qRound(font_width*self.scroll_decimal+border_width), self.h) - sax = qRound(self.w-font_width*self.scroll_decimal-border_width) + self.scrolling_area.resize( + qRound(font_width * self.scroll_decimal + border_width), self.h + ) + sax = qRound(self.w - font_width * self.scroll_decimal - border_width) self.scrolling_area.move(sax, 0) - prest = '0' * (self.total_decimals - self.scroll_decimal) + prest = "0" * (self.total_decimals - self.scroll_decimal) if self._bad or self._old: - prest = '' - self.pre_scroll_text = self.scene.addSimpleText (prest, self.f) + prest = "" + self.pre_scroll_text = self.scene.addSimpleText(prest, self.f) self.pre_scroll_text.setPen(QPen(QColor(Qt.white))) self.pre_scroll_text.setBrush(QBrush(QColor(Qt.white))) - - self.pre_scroll_text.setX (0) - self.pre_scroll_text.setY ((self.h-font_height)/2.0) - x = sax-border_width/2 - l = self.scene.addLine (x,0, x,top) + self.pre_scroll_text.setX(0) + self.pre_scroll_text.setY((self.h - font_height) / 2.0) + + x = sax - border_width / 2 + l = self.scene.addLine(x, 0, x, top) l.setPen(rect_pen) top += font_height - l = self.scene.addLine (x,top, x,self.h) + l = self.scene.addLine(x, top, x, self.h) l.setPen(rect_pen) - l = self.scene.addLine (x,0, self.w,0) + l = self.scene.addLine(x, 0, self.w, 0) l.setPen(rect_pen) - l = self.scene.addLine (x,self.h, self.w,self.h) + l = self.scene.addLine(x, self.h, self.w, self.h) l.setPen(rect_pen) # Get a failure scene ready in case it's needed self.fail_scene = QGraphicsScene(0, 0, self.w, self.h) - self.fail_scene.addRect(0,0, self.w, self.h, QPen(QColor(Qt.white)), QBrush(QColor(50,50,50))) + self.fail_scene.addRect( + 0, 0, self.w, self.h, QPen(QColor(Qt.white)), QBrush(QColor(50, 50, 50)) + ) warn_font = QFont(self.font_family, 10, QFont.Bold) t = self.fail_scene.addSimpleText("XXX", warn_font) - t.setPen (QPen(QColor(Qt.red))) - t.setBrush (QBrush(QColor(Qt.red))) + t.setPen(QPen(QColor(Qt.red))) + t.setBrush(QBrush(QColor(Qt.red))) r = t.boundingRect() - t.setPos ((self.w-r.width())/2, (self.h-r.height())/2) + t.setPos((self.w - r.width()) / 2, (self.h - r.height()) / 2) """ Not sure if this is needed: self.bad_text = self.scene.addSimpleText("BAD", warn_font) @@ -128,23 +142,23 @@ def resizeEvent(self, event): self.old_text.hide() """ - def redraw(self): - prevalue = int(self._value / (10 ** self.scroll_decimal)) - scroll_value = self._value - (prevalue * (10 ** self.scroll_decimal)) + prevalue = int(self._value / (10**self.scroll_decimal)) + scroll_value = self._value - (prevalue * (10**self.scroll_decimal)) if self.scroll_decimal > 1: - scroll_value = (scroll_value / (10 ** (self.scroll_decimal-1))) + scroll_value = scroll_value / (10 ** (self.scroll_decimal - 1)) prest = str(prevalue) prelen = self.total_decimals - self.scroll_decimal - prest = "{1:0{0}d}".format(prelen,prevalue) + prest = "{1:0{0}d}".format(prelen, prevalue) if scroll_value < 0: scroll_value = abs(scroll_value) if self._value < 0 and prevalue >= 0: # IF negative ensure the sign it displayed - prest = "-{1:0{0}d}".format(prelen-1,prevalue) + prest = "-{1:0{0}d}".format(prelen - 1, prevalue) if self._bad or self._old: - prest = '' + prest = "" self.pre_scroll_text.setText(prest) + print(dir(self.pre_scroll_text.text)) if not (self._bad or self._old or self._fail): self.scrolling_area.value = scroll_value @@ -159,8 +173,8 @@ def setValue(self, val): value = property(getValue, setValue) def flagDisplay(self): - if (self._bad or self._old or self._fail): - self.pre_scroll_text.setText('') + if self._bad or self._old or self._fail: + self.pre_scroll_text.setText("") self.scrolling_area.hide() else: self.pre_scroll_text.setBrush(QBrush(QColor(Qt.white))) @@ -169,30 +183,35 @@ def flagDisplay(self): def getBad(self): return self._bad + def setBad(self, b): if self._bad != b: self._bad = b - #if b: + # if b: # self.bad_text.show() - #else: + # else: # self.bad_text.hide() self.flagDisplay() + bad = property(getBad, setBad) def getOld(self): return self._old + def setOld(self, b): if self._old != b: self._old = b - #if b: + # if b: # self.old_text.show() - #else: + # else: # self.old_text.hide() self.flagDisplay() + old = property(getOld, setOld) def getFail(self): return self._fail + def setFail(self, b): if self._fail != b: self._fail = b @@ -201,8 +220,10 @@ def setFail(self, b): else: self.setScene(self.scene) self.flagDisplay() + fail = property(getFail, setFail) + class NumericalScrollDisplay(QGraphicsView): def __init__(self, parent=None, scroll_decimal=1, font_family="Sans", font_size=10): super(NumericalScrollDisplay, self).__init__() @@ -219,46 +240,47 @@ def resizeEvent(self, event): self.w = self.width() self.h = self.height() - t = QGraphicsSimpleTextItem ("9") - t.setFont (self.f) + t = QGraphicsSimpleTextItem("9") + t.setFont(self.f) font_width = t.boundingRect().width() font_height = t.boundingRect().height() self.digit_vertical_spacing = font_height * 0.8 nsh = self.digit_vertical_spacing * 12 + self.h - self.scene = QGraphicsScene(0,0,self.w, nsh) - self.scene.addRect(0, 0, self.w, nsh, - QPen(QColor(Qt.black)), QBrush(QColor(Qt.black))) + self.scene = QGraphicsScene(0, 0, self.w, nsh) + self.scene.addRect( + 0, 0, self.w, nsh, QPen(QColor(Qt.black)), QBrush(QColor(Qt.black)) + ) for i in range(20): - y = self.y_offset(i) - font_height/2 + y = self.y_offset(i) - font_height / 2 if y < 0: break - text = str(i%10) + text = str(i % 10) if len(text) < self.scroll_decimal: add0s = self.scroll_decimal - len(text) - text = text + "0"*add0s + text = text + "0" * add0s t = self.scene.addSimpleText(text, self.f) t.setX(2) t.setPen(QPen(QColor(Qt.white))) t.setBrush(QBrush(QColor(Qt.white))) t.setY(y) - for i in range(9,0,-1): - sv = i-10 - y = self.y_offset(sv) - font_height/2 - if y > nsh-font_height: + for i in range(9, 0, -1): + sv = i - 10 + y = self.y_offset(sv) - font_height / 2 + if y > nsh - font_height: break text = str(i) if len(text) < self.scroll_decimal: add0s = self.scroll_decimal - len(text) - text = text + "0"*add0s + text = text + "0" * add0s t = self.scene.addSimpleText(text, self.f) t.setPen(QPen(QColor(Qt.white))) t.setBrush(QBrush(QColor(Qt.white))) t.setX(2) t.setY(y) - self.setScene (self.scene) + self.setScene(self.scene) def y_offset(self, sv): - return ((10.0 - (sv)) * self.digit_vertical_spacing + self.h/2) + return (10.0 - (sv)) * self.digit_vertical_spacing + self.h / 2 def redraw(self): scroll_value = self._value diff --git a/src/pyefis/instruments/airspeed/__init__.py b/src/pyefis/instruments/airspeed/__init__.py index 5101ea7d..5d6c2769 100644 --- a/src/pyefis/instruments/airspeed/__init__.py +++ b/src/pyefis/instruments/airspeed/__init__.py @@ -26,9 +26,17 @@ from pyefis.instruments.NumericalDisplay import NumericalDisplay from pyefis.instruments import helpers + class Airspeed(QWidget): FULL_WIDTH = 400 - def __init__(self, parent=None, font_percent=0.07, bg_color=Qt.black, font_family="DejaVu Sans Condensed"): + + def __init__( + self, + parent=None, + font_percent=0.07, + bg_color=Qt.black, + font_family="DejaVu Sans Condensed", + ): super(Airspeed, self).__init__(parent) self.setStyleSheet("border: 0px") self.font_family = font_family @@ -44,16 +52,21 @@ def __init__(self, parent=None, font_percent=0.07, bg_color=Qt.black, font_famil self.item.failChanged[bool].connect(self.repaint) # V Speeds need to be init before paint - self.Vs = self.item.get_aux_value('Vs') - if self.Vs is None: self.Vs = 0 - self.Vs0 = self.item.get_aux_value('Vs0') - if self.Vs0 is None: self.Vs0 = 0 - self.Vno = self.item.get_aux_value('Vno') - if self.Vno is None: self.Vno = 0 - self.Vne = self.item.get_aux_value('Vne') - if self.Vne is None: self.Vne = 200 - self.Vfe = self.item.get_aux_value('Vfe') - if self.Vfe is None: self.Vfe = 0 + self.Vs = self.item.get_aux_value("Vs") + if self.Vs is None: + self.Vs = 0 + self.Vs0 = self.item.get_aux_value("Vs0") + if self.Vs0 is None: + self.Vs0 = 0 + self.Vno = self.item.get_aux_value("Vno") + if self.Vno is None: + self.Vno = 0 + self.Vne = self.item.get_aux_value("Vne") + if self.Vne is None: + self.Vne = 200 + self.Vfe = self.item.get_aux_value("Vfe") + if self.Vfe is None: + self.Vfe = 0 def getRatio(self): # Return X for 1:x specifying the ratio for this instrument @@ -68,7 +81,7 @@ def paintEvent(self, event): dial = QPainter(self) dial.setRenderHint(QPainter.Antialiasing) - #Draw the Black Background + # Draw the Black Background dial.fillRect(0, 0, w, h, QColor(self.bg_color)) # Setup Pens @@ -78,21 +91,21 @@ def paintEvent(self, event): fontMetrics = QFontMetricsF(f) dialPen = QPen(QColor(Qt.white)) - dialPen.setWidthF(s*0.01) + dialPen.setWidthF(s * 0.01) needleBrush = QBrush(QColor(Qt.white)) vnePen = QPen(QColor(Qt.red)) - vnePen.setWidthF(s*0.025) + vnePen.setWidthF(s * 0.025) vsoPen = QPen(QColor(Qt.white)) - vsoPen.setWidthF(s*0.015) + vsoPen.setWidthF(s * 0.015) vnoPen = QPen(QColor(Qt.green)) - vnoPen.setWidthF(s*0.015) + vnoPen.setWidthF(s * 0.015) yellowPen = QPen(QColor(Qt.yellow)) - yellowPen.setWidthF(s*0.015) + yellowPen.setWidthF(s * 0.015) # Dial Setup @@ -103,22 +116,24 @@ def paintEvent(self, event): Vno_angle = (-(((self.Vno - 30) * 2.5) + 25) + 90) * 16 Vne_angle = (-(((self.Vne - 30) * 2.5) + 25) + 90) * 16 - radius = int(round(min(w,h) * .45)) - diameter = radius*2 + radius = int(round(min(w, h) * 0.45)) + diameter = radius * 2 inner_offset = 3 - center_x = w/2 - center_y = h/2 + center_x = w / 2 + center_y = h / 2 # Vspeeds Arcs dial.setPen(vnoPen) - dial_rect = QRectF(center_x-radius, center_y-radius, - diameter, diameter) + dial_rect = QRectF(center_x - radius, center_y - radius, diameter, diameter) dial.drawArc(dial_rect, qRound(Vs_angle), qRound(-(Vs_angle - Vno_angle))) dial.setPen(vsoPen) - inner_rect = QRectF(center_x-radius+inner_offset, center_y-radius+inner_offset, - diameter-inner_offset*3, diameter-inner_offset*3) - dial.drawArc(inner_rect, - qRound(Vs0_angle), qRound(-(Vs0_angle - Vfe_angle))) + inner_rect = QRectF( + center_x - radius + inner_offset, + center_y - radius + inner_offset, + diameter - inner_offset * 3, + diameter - inner_offset * 3, + ) + dial.drawArc(inner_rect, qRound(Vs0_angle), qRound(-(Vs0_angle - Vfe_angle))) dial.setPen(yellowPen) dial.drawArc(dial_rect, qRound(Vno_angle), qRound(-(Vno_angle - Vne_angle))) dial.save() @@ -129,21 +144,20 @@ def paintEvent(self, event): a_s = 0 while count < 360: if count % 25 == 0 and a_s <= 140: - dial.drawLine(0, -radius, 0, -(radius-15)) + dial.drawLine(0, -radius, 0, -(radius - 15)) x = fontMetrics.width(str(a_s)) / 2 y = f.pixelSize() - dial.drawText(qRound(-x), qRound(-(radius-15 - y)), - str(a_s)) + dial.drawText(qRound(-x), qRound(-(radius - 15 - y)), str(a_s)) a_s += 10 if count == 0: a_s = 30 count = count + 19 dial.rotate(19) elif count % 12.5 == 0 and a_s <= 140: - dial.drawLine(0, -(radius), 0, -(radius-10)) + dial.drawLine(0, -(radius), 0, -(radius - 10)) if count == (-Vne_angle / 16) + 90: dial.setPen(vnePen) - dial.drawLine(0, -(radius), 0, -(radius-15)) + dial.drawLine(0, -(radius), 0, -(radius - 15)) dial.setPen(dialPen) dial.rotate(0.5) count += 0.5 @@ -151,10 +165,10 @@ def paintEvent(self, event): if self.item.fail: warn_font = QFont(self.font_family, 30, QFont.Bold) dial.resetTransform() - dial.setPen (QPen(QColor(Qt.red))) - dial.setBrush (QBrush(QColor(Qt.red))) - dial.setFont (warn_font) - dial.drawText (0,0,w,h, Qt.AlignCenter, "XXX") + dial.setPen(QPen(QColor(Qt.red))) + dial.setBrush(QBrush(QColor(Qt.red))) + dial.setFont(warn_font) + dial.drawText(0, 0, w, h, Qt.AlignCenter, "XXX") dial.restore() return @@ -165,9 +179,10 @@ def paintEvent(self, event): else: dial.setPen(QPen(QColor(Qt.white))) dial.setBrush(QBrush(QColor(Qt.white))) - #Needle Movement - needle = QPolygon([QPoint(5, 0), QPoint(0, +5), QPoint(-5, 0), - QPoint(0, -(radius-15))]) + # Needle Movement + needle = QPolygon( + [QPoint(5, 0), QPoint(0, +5), QPoint(-5, 0), QPoint(0, -(radius - 15))] + ) if self.airspeed <= 30: # Airspeeds Below 30 Knots needle_angle = self._airspeed * 0.83 @@ -204,18 +219,20 @@ def setAirspeed(self, airspeed): airspeed = property(getAirspeed, setAirspeed) - def setAsOld(self,b): + def setAsOld(self, b): pass - def setAsBad(self,b): + def setAsBad(self, b): pass - def setAsFail(self,b): + def setAsFail(self, b): pass class Airspeed_Tape(QGraphicsView): - def __init__(self, parent=None, font_percent=None,font_family="DejaVu Sans Condensed"): + def __init__( + self, parent=None, font_percent=None, font_family="DejaVu Sans Condensed" + ): super(Airspeed_Tape, self).__init__(parent) self.myparent = parent self.font_family = font_family @@ -232,22 +249,27 @@ def __init__(self, parent=None, font_percent=None,font_family="DejaVu Sans Conde self._airspeed = self.item.value # V Speeds - self.Vs = self.item.get_aux_value('Vs') - if self.Vs is None: self.Vs = 0 - self.Vs0 = self.item.get_aux_value('Vs0') - if self.Vs0 is None: self.Vs0 = 0 - self.Vno = self.item.get_aux_value('Vno') - if self.Vno is None: self.Vno = 0 - self.Vne = self.item.get_aux_value('Vne') - if self.Vne is None: self.Vne = 200 - self.Vfe = self.item.get_aux_value('Vfe') - if self.Vfe is None: self.Vfe = 0 - - self.max = int(round(self.Vne*1.25)) + self.Vs = self.item.get_aux_value("Vs") + if self.Vs is None: + self.Vs = 0 + self.Vs0 = self.item.get_aux_value("Vs0") + if self.Vs0 is None: + self.Vs0 = 0 + self.Vno = self.item.get_aux_value("Vno") + if self.Vno is None: + self.Vno = 0 + self.Vne = self.item.get_aux_value("Vne") + if self.Vne is None: + self.Vne = 200 + self.Vfe = self.item.get_aux_value("Vfe") + if self.Vfe is None: + self.Vfe = 0 + + self.max = int(round(self.Vne * 1.25)) self.backgroundOpacity = 0.3 self.foregroundOpacity = 0.6 - self.pph = 10 # Pixels per unit + self.pph = 10 # Pixels per unit self.fontsize = 15 self.majorDiv = 10 self.minorDiv = 5 @@ -262,71 +284,96 @@ def resizeEvent(self, event): self.markWidth = w / 5 f = QFont(self.font_family) if self.font_mask: - self.fontsize = helpers.fit_to_mask(self.width()*.50,self.height()*0.05,self.font_mask,self.font_family) + self.fontsize = helpers.fit_to_mask( + self.width() * 0.50, + self.height() * 0.05, + self.font_mask, + self.font_family, + ) f.setPointSizeF(self.fontsize) else: f.setPixelSize(self.fontsize) tape_height = self.max * self.pph + h - tape_start = self.max * self.pph + h/2 + tape_start = self.max * self.pph + h / 2 dialPen = QPen(QColor(Qt.white)) self.scene = QGraphicsScene(0, 0, w, tape_height) - x = self.scene.addRect(0, 0, w, tape_height, - QPen(QColor(32, 32, 32)), QBrush(QColor(32, 32, 32))) + x = self.scene.addRect( + 0, 0, w, tape_height, QPen(QColor(32, 32, 32)), QBrush(QColor(32, 32, 32)) + ) x.setOpacity(self.backgroundOpacity) # Add Markings # Green Bar - r = QRectF(QPointF(0, -self.Vno * self.pph + tape_start), - QPointF(self.markWidth, -self.Vs0 * self.pph + tape_start)) - x = self.scene.addRect(r, QPen(QColor(0,155,0)), QBrush(QColor(0,155,0))) + r = QRectF( + QPointF(0, -self.Vno * self.pph + tape_start), + QPointF(self.markWidth, -self.Vs0 * self.pph + tape_start), + ) + x = self.scene.addRect(r, QPen(QColor(0, 155, 0)), QBrush(QColor(0, 155, 0))) x.setOpacity(self.foregroundOpacity) # White Bar - r = QRectF(QPointF(self.markWidth / 2, -self.Vfe * self.pph + tape_start), - QPointF(self.markWidth, -self.Vs0 * self.pph + tape_start)) + r = QRectF( + QPointF(self.markWidth / 2, -self.Vfe * self.pph + tape_start), + QPointF(self.markWidth, -self.Vs0 * self.pph + tape_start), + ) x = self.scene.addRect(r, QPen(Qt.white), QBrush(Qt.white)) x.setOpacity(self.foregroundOpacity) - # Yellow Bar - r = QRectF(QPointF(0, -self.Vno * self.pph + tape_start), - QPointF(self.markWidth, -self.Vne * self.pph + tape_start)) + r = QRectF( + QPointF(0, -self.Vno * self.pph + tape_start), + QPointF(self.markWidth, -self.Vne * self.pph + tape_start), + ) x = self.scene.addRect(r, QPen(Qt.yellow), QBrush(Qt.yellow)) x.setOpacity(self.foregroundOpacity) # Draw the little white lines and the text for i in range(self.max, -1, -1): if i % self.majorDiv == 0: - l = self.scene.addLine(0, (- i * self.pph) + tape_start, w / 2, - (- i * self.pph) + tape_start, dialPen) + l = self.scene.addLine( + 0, + (-i * self.pph) + tape_start, + w / 2, + (-i * self.pph) + tape_start, + dialPen, + ) l.setOpacity(self.foregroundOpacity) t = self.scene.addText(str(i)) t.setFont(f) self.scene.setFont(f) t.setDefaultTextColor(QColor(Qt.white)) t.setX(w - t.boundingRect().width()) - t.setY(((- i * self.pph) + tape_start) - - t.boundingRect().height() / 2) + t.setY(((-i * self.pph) + tape_start) - t.boundingRect().height() / 2) t.setOpacity(self.foregroundOpacity) - elif i % self.minorDiv ==0: - l = self.scene.addLine(0, (- i * self.pph) + tape_start, - w / 3, (- i * self.pph) + tape_start, dialPen) + elif i % self.minorDiv == 0: + l = self.scene.addLine( + 0, + (-i * self.pph) + tape_start, + w / 3, + (-i * self.pph) + tape_start, + dialPen, + ) l.setOpacity(self.foregroundOpacity) # Red Line vnePen = QPen(QColor(Qt.red)) vnePen.setWidth(4) - l = self.scene.addLine(0, -self.Vne * self.pph + tape_start, - 30, -self.Vne * self.pph + tape_start, vnePen) + l = self.scene.addLine( + 0, + -self.Vne * self.pph + tape_start, + 30, + -self.Vne * self.pph + tape_start, + vnePen, + ) l.setOpacity(self.foregroundOpacity) self.numerical_display = NumericalDisplay(self) - nbh = w/2 - self.numerical_display.resize (qRound(w/2), qRound(nbh)) - self.numeric_box_pos = QPoint(qRound(w-w/2), qRound(h/2-nbh/2)) + nbh = w / 2 + self.numerical_display.resize(qRound(w / 2), qRound(nbh)) + self.numeric_box_pos = QPoint(qRound(w - w / 2), qRound(h / 2 - nbh / 2)) self.numerical_display.move(self.numeric_box_pos) - self.numeric_box_pos.setY(qRound(self.numeric_box_pos.y()+nbh/2)) + self.numeric_box_pos.setY(qRound(self.numeric_box_pos.y() + nbh / 2)) self.numerical_display.show() self.numerical_display.value = self._airspeed self.setAsOld(self.item.old) @@ -334,8 +381,7 @@ def resizeEvent(self, event): self.setAsFail(self.item.fail) self.setScene(self.scene) - self.centerOn(self.scene.width() / 2, - -self._airspeed * self.pph + tape_start) + self.centerOn(self.scene.width() / 2, -self._airspeed * self.pph + tape_start) self.item.valueChanged[float].connect(self.setAirspeed) self.item.oldChanged[bool].connect(self.setAsOld) self.item.badChanged[bool].connect(self.setAsBad) @@ -344,11 +390,10 @@ def resizeEvent(self, event): def redraw(self): if not self.isVisible(): return - tape_start = self.max * self.pph + self.height()/2 + tape_start = self.max * self.pph + self.height() / 2 self.resetTransform() - self.centerOn(self.scene.width() / 2, - -self._airspeed * self.pph + tape_start) + self.centerOn(self.scene.width() / 2, -self._airspeed * self.pph + tape_start) self.numerical_display.value = self._airspeed # Index Line that doesn't move to make it easy to read the airspeed. @@ -363,10 +408,16 @@ def paintEvent(self, event): p.translate(self.numeric_box_pos.x(), self.numeric_box_pos.y()) p.setPen(marks) p.setBrush(QBrush(Qt.black)) - triangle_size = w/8 - p.drawConvexPolygon(QPolygon([QPoint(0, qRound(-triangle_size-3)), - QPoint(0, qRound(triangle_size-2)), - QPoint(qRound(-triangle_size), -1)])) + triangle_size = w / 8 + p.drawConvexPolygon( + QPolygon( + [ + QPoint(0, qRound(-triangle_size - 3)), + QPoint(0, qRound(triangle_size - 2)), + QPoint(qRound(-triangle_size), -1), + ] + ) + ) def getAirspeed(self): return self._airspeed @@ -378,13 +429,13 @@ def setAirspeed(self, airspeed): airspeed = property(getAirspeed, setAirspeed) - def setAsOld(self,b): + def setAsOld(self, b): self.numerical_display.old = b - def setAsBad(self,b): + def setAsBad(self, b): self.numerical_display.bad = b - def setAsFail(self,b): + def setAsFail(self, b): self.numerical_display.fail = b # We don't want this responding to keystrokes @@ -398,8 +449,9 @@ def wheelEvent(self, event): class Airspeed_Box(QWidget): """Represents a simple numeric display type gauge. The benefit of using this - over a normal text display is that this will change colors properly when - limits are reached or when failures occur""" + over a normal text display is that this will change colors properly when + limits are reached or when failures occur""" + def __init__(self, parent=None, font_family="DejaVu Sans Condensed"): super(Airspeed_Box, self).__init__(parent) self.font_family = font_family @@ -411,7 +463,7 @@ def __init__(self, parent=None, font_family="DejaVu Sans Condensed"): self.fix_item.valueChanged[float].connect(self.setASData) self.alignment = Qt.AlignLeft | Qt.AlignVCenter - self.valueAlignment = Qt.AlignRight | Qt.AlignVCenter + self.valueAlignment = Qt.AlignRight | Qt.AlignVCenter self.small_font_percent = 0.4 self.color = Qt.white self.modeText = self.modes[self._modeIndicator] @@ -424,9 +476,10 @@ def resizeEvent(self, event): self.smallFont.setPixelSize(qRound(self.height() * self.small_font_percent)) qm = QFontMetrics(self.smallFont) - self.modeTextRect = QRectF(0, 0, self.width()-5, self.height()*0.4) - self.valueTextRect = QRectF(0, self.height()*0.5, - self.width()-5, self.height()*0.4) + self.modeTextRect = QRectF(0, 0, self.width() - 5, self.height() * 0.4) + self.valueTextRect = QRectF( + 0, self.height() * 0.5, self.width() - 5, self.height() * 0.4 + ) def paintEvent(self, event): p = QPainter(self) @@ -444,7 +497,7 @@ def paintEvent(self, event): opt = QTextOption(self.alignment) p.drawText(self.modeTextRect, self.modeText, opt) - #Draw Value + # Draw Value p.setFont(self.smallFont) opt = QTextOption(self.valueAlignment) p.drawText(self.valueTextRect, self.valueText, opt) @@ -453,7 +506,8 @@ def setMode(self, Mode): if Mode == "": self.fix_item.valueChanged[float].disconnect(self.setASData) self._modeIndicator += 1 - if self._modeIndicator == 3: self._modeIndicator = 0 + if self._modeIndicator == 3: + self._modeIndicator = 0 else: if Mode != self._modeIndicator: self.fix_item.valueChanged[float].disconnect(self.setASData) From ca9716f87a38ffce3db978e8566d84c286d97603 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Thu, 13 Jun 2024 21:53:11 -0400 Subject: [PATCH 41/60] Added tests for airspeed --- tests/instruments/airspeed/__init__.py | 0 tests/instruments/airspeed/test_airspeed.py | 90 +++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 tests/instruments/airspeed/__init__.py create mode 100644 tests/instruments/airspeed/test_airspeed.py diff --git a/tests/instruments/airspeed/__init__.py b/tests/instruments/airspeed/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/instruments/airspeed/test_airspeed.py b/tests/instruments/airspeed/test_airspeed.py new file mode 100644 index 00000000..c925995d --- /dev/null +++ b/tests/instruments/airspeed/test_airspeed.py @@ -0,0 +1,90 @@ +import pytest +from unittest import mock +from PyQt5.QtWidgets import QApplication +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QColor, QBrush +from pyefis.instruments import airspeed #import Airspeed, Airspeed_Tape, Airspeed_Box +import pyavtools.fix as fix +import pyefis.hmi as hmi + +@pytest.fixture +def app(qtbot): + test_app = QApplication.instance() + if test_app is None: + test_app = QApplication([]) + return test_app + + +def test_numerical_airspeed(qtbot): + fix.db.get_item("IAS").set_aux_value("Vs",45.0) + fix.db.get_item("IAS").set_aux_value("Vs0",40.0) + fix.db.get_item("IAS").set_aux_value("Vno",125.0) + fix.db.get_item("IAS").set_aux_value("Vne",140.0) + fix.db.get_item("IAS").set_aux_value("Vfe",70.0) + fix.db.set_value("IAS", "100") + widget = airspeed.Airspeed() + assert widget.getRatio() == 1 + qtbot.addWidget(widget) + widget.resize(201,200) + widget.show() + qtbot.waitExposed(widget) + qtbot.wait(500) + assert widget.item.key == "IAS" + assert widget.Vs == 45 + fix.db.get_item("IAS").fail = True + fix.db.get_item("IAS").fail = False + fix.db.get_item("IAS").old = True + fix.db.get_item("IAS").old = False + fix.db.set_value("IAS", "20") + widget.setAsOld(True) + widget.setAsBad(True) + widget.setAsFail(True) + assert widget.getAirspeed() == 20 + qtbot.wait(200) + +def test_numerical_airspeed_tape(qtbot): + widget = airspeed.Airspeed_Tape(font_percent=0.5) + qtbot.addWidget(widget) + widget.redraw() + widget.resize(50,200) + widget.show() + assert widget.Vs0 == 40 + assert widget.getAirspeed() == 20 + widget.setAirspeed(40) #redraw() + widget.keyPressEvent(None) + widget.wheelEvent(None) + + +def test_numerical_airspeed_box(qtbot): + hmi.initialize({}) + widget = airspeed.Airspeed_Box() + qtbot.addWidget(widget) + widget.resize(50,50) + widget.show() + fix.db.set_value("TAS", 100) + widget.setMode(1) + widget.setMode(0) + assert widget.modeText == "TAS" + assert widget.valueText == "100" + fix.db.set_value("GS", 80) + widget.setMode(1) + assert widget.modeText == "GS" + assert widget.valueText == "80" + assert widget._modeIndicator == 1 + fix.db.set_value("IAS", 140) + widget.setMode(2) + assert widget.modeText == "IAS" + assert widget.valueText == "140" + widget.setMode("") + assert widget._modeIndicator == 0 + + fix.db.get_item("TAS").fail = True + fix.db.set_value("TAS", 101) + assert widget.valueText == "XXX" + fix.db.get_item("TAS").fail = False + fix.db.get_item("TAS").bad = True + fix.db.set_value("TAS", 102) + assert widget.valueText == "" + + qtbot.wait(2000) + From 2fae2d922c7807d23b9ed973b7a71985313e20f2 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Thu, 13 Jun 2024 21:53:55 -0400 Subject: [PATCH 42/60] Formatted airspeed test with black --- tests/instruments/airspeed/test_airspeed.py | 27 +++++++++++---------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/tests/instruments/airspeed/test_airspeed.py b/tests/instruments/airspeed/test_airspeed.py index c925995d..c8804fe4 100644 --- a/tests/instruments/airspeed/test_airspeed.py +++ b/tests/instruments/airspeed/test_airspeed.py @@ -3,10 +3,11 @@ from PyQt5.QtWidgets import QApplication from PyQt5.QtCore import Qt from PyQt5.QtGui import QColor, QBrush -from pyefis.instruments import airspeed #import Airspeed, Airspeed_Tape, Airspeed_Box +from pyefis.instruments import airspeed # import Airspeed, Airspeed_Tape, Airspeed_Box import pyavtools.fix as fix import pyefis.hmi as hmi + @pytest.fixture def app(qtbot): test_app = QApplication.instance() @@ -16,16 +17,16 @@ def app(qtbot): def test_numerical_airspeed(qtbot): - fix.db.get_item("IAS").set_aux_value("Vs",45.0) - fix.db.get_item("IAS").set_aux_value("Vs0",40.0) - fix.db.get_item("IAS").set_aux_value("Vno",125.0) - fix.db.get_item("IAS").set_aux_value("Vne",140.0) - fix.db.get_item("IAS").set_aux_value("Vfe",70.0) + fix.db.get_item("IAS").set_aux_value("Vs", 45.0) + fix.db.get_item("IAS").set_aux_value("Vs0", 40.0) + fix.db.get_item("IAS").set_aux_value("Vno", 125.0) + fix.db.get_item("IAS").set_aux_value("Vne", 140.0) + fix.db.get_item("IAS").set_aux_value("Vfe", 70.0) fix.db.set_value("IAS", "100") widget = airspeed.Airspeed() assert widget.getRatio() == 1 qtbot.addWidget(widget) - widget.resize(201,200) + widget.resize(201, 200) widget.show() qtbot.waitExposed(widget) qtbot.wait(500) @@ -36,30 +37,31 @@ def test_numerical_airspeed(qtbot): fix.db.get_item("IAS").old = True fix.db.get_item("IAS").old = False fix.db.set_value("IAS", "20") - widget.setAsOld(True) + widget.setAsOld(True) widget.setAsBad(True) widget.setAsFail(True) assert widget.getAirspeed() == 20 qtbot.wait(200) + def test_numerical_airspeed_tape(qtbot): widget = airspeed.Airspeed_Tape(font_percent=0.5) qtbot.addWidget(widget) widget.redraw() - widget.resize(50,200) + widget.resize(50, 200) widget.show() assert widget.Vs0 == 40 assert widget.getAirspeed() == 20 - widget.setAirspeed(40) #redraw() + widget.setAirspeed(40) # redraw() widget.keyPressEvent(None) - widget.wheelEvent(None) + widget.wheelEvent(None) def test_numerical_airspeed_box(qtbot): hmi.initialize({}) widget = airspeed.Airspeed_Box() qtbot.addWidget(widget) - widget.resize(50,50) + widget.resize(50, 50) widget.show() fix.db.set_value("TAS", 100) widget.setMode(1) @@ -87,4 +89,3 @@ def test_numerical_airspeed_box(qtbot): assert widget.valueText == "" qtbot.wait(2000) - From fd024c76d5a0e4d235126ea04af2727ab88d2eb4 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Thu, 13 Jun 2024 21:54:47 -0400 Subject: [PATCH 43/60] Added airspeed fix keys to conftest.py --- conftest.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/conftest.py b/conftest.py index 9f688b6e..e5de8e88 100644 --- a/conftest.py +++ b/conftest.py @@ -47,6 +47,25 @@ def create_numbers(key,value,old=False,bad=False,fail=False,annunciate=False): create_numbers("NUMHIGHALARM", 95.19) +fix.db.define_item("GS", "GS", 'float', 0.0, 2000.0, 'knots', 50000, "") +fix.db.set_value("GS", 100) +fix.db.get_item("GS").bad = False +fix.db.get_item("GS").fail = False + +fix.db.define_item("TAS", "TAS", 'float', 0.0, 2000.0, 'knots', 50000, "") +fix.db.set_value("TAS", 105) +fix.db.get_item("TAS").bad = False +fix.db.get_item("TAS").fail = False + +fix.db.define_item("IAS", "Number", 'float', 0, 2000.0, 'knots', 50000, "Min,Max,V1,V2,Vne,Vfe,Vmc,Va,Vno,Vs,Vs0,Vx,Vy") +fix.db.set_value("IAS", "110") +fix.db.get_item("IAS").bad = False +fix.db.get_item("IAS").fail = False +fix.db.get_item("IAS").set_aux_value("Vs",45.0) +fix.db.get_item("IAS").set_aux_value("Vs0",40.0) +fix.db.get_item("IAS").set_aux_value("Vno",125.0) +fix.db.get_item("IAS").set_aux_value("Vne",140.0) +fix.db.get_item("IAS").set_aux_value("Vfe",70.0) From b553eb44f7da5509ed37f14953a4ed9832bd6e28 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Thu, 13 Jun 2024 21:55:46 -0400 Subject: [PATCH 44/60] Formatted conftest.py with black --- conftest.py | 62 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/conftest.py b/conftest.py index e5de8e88..dcac4e42 100644 --- a/conftest.py +++ b/conftest.py @@ -2,19 +2,21 @@ import sys import tests.mock_db.client import tests.mock_db.scheduler + sys.modules["pyavtools.fix.client"] = tests.mock_db.client sys.modules["pyavtools.scheduler"] = tests.mock_db.scheduler # import fix import pyavtools.fix as fix + # Init database -fix.initialize({"main":{"FixServer":"localhost","FixPort":"3490"}}) +fix.initialize({"main": {"FixServer": "localhost", "FixPort": "3490"}}) # TODO Add example setting aux values since needed on many tests # Define some fix db items we can use in tests # Define a key: # key,desc, dtype(as a string), min, max, units, tol, aux -fix.db.define_item("TEST", "Testing key", 'float', 0, 100, '', 50000, "") +fix.db.define_item("TEST", "Testing key", "float", 0, 100, "", 50000, "") # Set initial Value fix.db.set_value("TEST", 50.19) # Bad and fail are true by default to set them False @@ -22,50 +24,66 @@ fix.db.get_item("TEST").fail = False -def create_numbers(key,value,old=False,bad=False,fail=False,annunciate=False): - fix.db.define_item(key, "Number", 'float', 0, 100, 'degC', 50000, "Min,Max,highWarn,highAlarm,lowWarn,lowAlarm") +def create_numbers(key, value, old=False, bad=False, fail=False, annunciate=False): + fix.db.define_item( + key, + "Number", + "float", + 0, + 100, + "degC", + 50000, + "Min,Max,highWarn,highAlarm,lowWarn,lowAlarm", + ) fix.db.set_value(key, value) fix.db.get_item(key).old = old fix.db.get_item(key).bad = bad fix.db.get_item(key).fail = fail fix.db.get_item(key).annunciate = annunciate - fix.db.get_item(key).set_aux_value("Min",0) - fix.db.get_item(key).set_aux_value("lowAlarm",10) - fix.db.get_item(key).set_aux_value("lowWarn",20) - fix.db.get_item(key).set_aux_value("highWarn",80) - fix.db.get_item(key).set_aux_value("highAlarm",90) - fix.db.get_item(key).set_aux_value("Max",100) + fix.db.get_item(key).set_aux_value("Min", 0) + fix.db.get_item(key).set_aux_value("lowAlarm", 10) + fix.db.get_item(key).set_aux_value("lowWarn", 20) + fix.db.get_item(key).set_aux_value("highWarn", 80) + fix.db.get_item(key).set_aux_value("highAlarm", 90) + fix.db.get_item(key).set_aux_value("Max", 100) + create_numbers("NUMOK", 50.91) create_numbers("NUMOLD", 51.82, old=True) create_numbers("NUMBAD", 48.73, bad=True) create_numbers("NUMFAIL", 65.64, fail=True) create_numbers("NUMANNUNCIATE", 62.55, annunciate=True) -create_numbers("NUMLOWWARN",18.46) +create_numbers("NUMLOWWARN", 18.46) create_numbers("NUMLOWALARM", 5.37) create_numbers("NUMHIGHWARN", 81.28) create_numbers("NUMHIGHALARM", 95.19) -fix.db.define_item("GS", "GS", 'float', 0.0, 2000.0, 'knots', 50000, "") +fix.db.define_item("GS", "GS", "float", 0.0, 2000.0, "knots", 50000, "") fix.db.set_value("GS", 100) fix.db.get_item("GS").bad = False fix.db.get_item("GS").fail = False -fix.db.define_item("TAS", "TAS", 'float', 0.0, 2000.0, 'knots', 50000, "") +fix.db.define_item("TAS", "TAS", "float", 0.0, 2000.0, "knots", 50000, "") fix.db.set_value("TAS", 105) fix.db.get_item("TAS").bad = False fix.db.get_item("TAS").fail = False -fix.db.define_item("IAS", "Number", 'float', 0, 2000.0, 'knots', 50000, "Min,Max,V1,V2,Vne,Vfe,Vmc,Va,Vno,Vs,Vs0,Vx,Vy") +fix.db.define_item( + "IAS", + "Number", + "float", + 0, + 2000.0, + "knots", + 50000, + "Min,Max,V1,V2,Vne,Vfe,Vmc,Va,Vno,Vs,Vs0,Vx,Vy", +) fix.db.set_value("IAS", "110") fix.db.get_item("IAS").bad = False fix.db.get_item("IAS").fail = False -fix.db.get_item("IAS").set_aux_value("Vs",45.0) -fix.db.get_item("IAS").set_aux_value("Vs0",40.0) -fix.db.get_item("IAS").set_aux_value("Vno",125.0) -fix.db.get_item("IAS").set_aux_value("Vne",140.0) -fix.db.get_item("IAS").set_aux_value("Vfe",70.0) - - - +fix.db.get_item("IAS").set_aux_value("Vs", 45.0) +fix.db.get_item("IAS").set_aux_value("Vs0", 40.0) +fix.db.get_item("IAS").set_aux_value("Vno", 125.0) +fix.db.get_item("IAS").set_aux_value("Vne", 140.0) +fix.db.get_item("IAS").set_aux_value("Vfe", 70.0) From 1aa5e6788ea0b30414c1fae224f5cab36ac37d25 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Thu, 13 Jun 2024 21:56:38 -0400 Subject: [PATCH 45/60] Changed size of virtual screen when running tests --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 00c94d82..22521d22 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,7 +62,8 @@ Source = "https://github.com/makerplane/pyEfis" [tool.pytest.ini_options] qt_api="pyqt5" env = [ - "QT_QPA_PLATFORM = offscreen" + "QT_QPA_PLATFORM = offscreen:size=1000x1000" +# "QT_QPA_PLATFORM = xcb:size=1000,1000" ] filterwarnings = [ # I belive this is warning that pyqt needs updated if you update to python 3.12 From b604993e44d23a61bd4c0436cc350f3f9a6b08e5 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Thu, 13 Jun 2024 21:57:48 -0400 Subject: [PATCH 46/60] Updated screen builder grid overlay so the lines are semi-transparent --- src/pyefis/screens/screenbuilder.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/pyefis/screens/screenbuilder.py b/src/pyefis/screens/screenbuilder.py index fa44e79a..bfa7add5 100644 --- a/src/pyefis/screens/screenbuilder.py +++ b/src/pyefis/screens/screenbuilder.py @@ -587,7 +587,6 @@ def get_grid_coordinates(self, column, row ): grid_width = ( self.width() - leftm - rightm ) / int(self.layout['columns']) grid_height = ( self.height() - topm - bottomm ) / int(self.layout['rows']) grid_x = leftm + grid_width * (( column )) - #print(f"leftm:{type(leftm)}, grid_width:{type(grid_width)} column:{column}") grid_y = topm + grid_height * (( row )) return grid_x, grid_y, grid_width, grid_height @@ -933,26 +932,26 @@ def paintEvent(self,event): self.textRect = QRectF(int(0), int(0), qRound(grid_width), qRound(grid_height)) while x <= int(self.layout['columns']): grid_x = leftm + grid_width * (x) - painter.setPen(QPen(Qt.red,1)) + painter.setPen(QPen(QColor("#99ff0000"),1)) if (x) % 10 == 0: - painter.setPen(QPen(Qt.green,3)) + painter.setPen(QPen(QColor("#9900ff00"),3)) self.textRect = QRectF(grid_x, grid_y, grid_width*10, grid_height*5) painter.drawText(self.textRect, str(x),QTextOption(Qt.AlignCenter)) - painter.setPen(QPen(Qt.red,3)) + painter.setPen(QPen(QColor("#99ff0000"),3)) painter.drawLine(qRound(grid_x), qRound(grid_y), qRound(grid_x), self.height()) x += 5 y = 0 grid_x = leftm while y <= int(self.layout['rows']): grid_y = topm + grid_height * (y) - painter.setPen(QPen(Qt.red,1)) + painter.setPen(QPen(QColor("#99ff0000"),1)) if (y) % 10 == 0: if y > 0: - painter.setPen(QPen(Qt.green,3)) + painter.setPen(QPen(QColor("#9900ff00"),3)) self.textRect = QRectF(grid_x, grid_y, grid_width*10, grid_height*5) painter.drawText(self.textRect, str(y),QTextOption(Qt.AlignCenter)) - painter.setPen(QPen(Qt.red,3)) + painter.setPen(QPen(QColor("#99ff0000"),3)) painter.drawLine(qRound(grid_x), qRound(grid_y), self.width(), qRound(grid_y)) y += 5 From 7ff687717409c082fb1a1e2de94fdf347b53aae8 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Thu, 13 Jun 2024 21:58:22 -0400 Subject: [PATCH 47/60] Removed unneeded imports on instruments.misc --- tests/instruments/misc/test_misc.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/instruments/misc/test_misc.py b/tests/instruments/misc/test_misc.py index 486d21a8..65d58681 100644 --- a/tests/instruments/misc/test_misc.py +++ b/tests/instruments/misc/test_misc.py @@ -6,8 +6,6 @@ from PyQt5.QtGui import QColor from pyefis.instruments.misc import StaticText, ValueDisplay -import time -import subprocess import os import pyavtools.fix as fix From 57937de3568455651757e486101579756aa2b89d Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Thu, 13 Jun 2024 21:59:36 -0400 Subject: [PATCH 48/60] Removed wait at end of airspeed test --- tests/instruments/airspeed/test_airspeed.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/instruments/airspeed/test_airspeed.py b/tests/instruments/airspeed/test_airspeed.py index c8804fe4..149c4102 100644 --- a/tests/instruments/airspeed/test_airspeed.py +++ b/tests/instruments/airspeed/test_airspeed.py @@ -88,4 +88,3 @@ def test_numerical_airspeed_box(qtbot): fix.db.set_value("TAS", 102) assert widget.valueText == "" - qtbot.wait(2000) From bdd5a1cfc8df07cef9c38a55af306ba3911065b2 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Thu, 13 Jun 2024 22:02:25 -0400 Subject: [PATCH 49/60] Removed unneeded code --- tests/instruments/airspeed/test_airspeed.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/instruments/airspeed/test_airspeed.py b/tests/instruments/airspeed/test_airspeed.py index 149c4102..90cee4ae 100644 --- a/tests/instruments/airspeed/test_airspeed.py +++ b/tests/instruments/airspeed/test_airspeed.py @@ -3,7 +3,7 @@ from PyQt5.QtWidgets import QApplication from PyQt5.QtCore import Qt from PyQt5.QtGui import QColor, QBrush -from pyefis.instruments import airspeed # import Airspeed, Airspeed_Tape, Airspeed_Box +from pyefis.instruments import airspeed import pyavtools.fix as fix import pyefis.hmi as hmi From 4940cece46267b5402a91a377bfad10562538ea3 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Thu, 13 Jun 2024 22:23:13 -0400 Subject: [PATCH 50/60] Saving progress on arc gauge test --- tests/instruments/gauges/test_arc.py | 38 ++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/instruments/gauges/test_arc.py diff --git a/tests/instruments/gauges/test_arc.py b/tests/instruments/gauges/test_arc.py new file mode 100644 index 00000000..9e829f81 --- /dev/null +++ b/tests/instruments/gauges/test_arc.py @@ -0,0 +1,38 @@ +import pytest +from unittest import mock +from PyQt5.QtWidgets import QApplication +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QColor, QBrush +from pyefis.instruments import gauges +import pyavtools.fix as fix +import pyefis.hmi as hmi + + +@pytest.fixture +def app(qtbot): + test_app = QApplication.instance() + if test_app is None: + test_app = QApplication([]) + return test_app + +def test_arc_gauge(qtbot): + widget = gauges.ArcGauge() + assert widget.getRatio() == 2 + widget.setDbkey("NUMOK") + widget.setupGauge() + qtbot.addWidget(widget) + widget.resize(201, 200) + widget.show() + qtbot.waitExposed(widget) + widget.resize(200, 220) + widget.resizeEvent(None) + assert widget.tlcx == 0 + assert widget.tlcy == 60 + widget.resize(400, 100) + widget.resizeEvent(None) + assert widget.tlcx == 100 + assert widget.tlcy == 00 + widget.font_mask = "0000" + + qtbot.wait(5000) + From 503ce64eb3a3e9f4b9067fb419951c8d13e0c760 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Fri, 14 Jun 2024 14:05:47 -0400 Subject: [PATCH 51/60] Tests for arc gauge but lacking assertions, likely need to add some mocks to better test --- tests/instruments/gauges/__init__.py | 0 tests/instruments/gauges/test_arc.py | 85 +++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 tests/instruments/gauges/__init__.py diff --git a/tests/instruments/gauges/__init__.py b/tests/instruments/gauges/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/instruments/gauges/test_arc.py b/tests/instruments/gauges/test_arc.py index 9e829f81..9084be87 100644 --- a/tests/instruments/gauges/test_arc.py +++ b/tests/instruments/gauges/test_arc.py @@ -2,7 +2,7 @@ from unittest import mock from PyQt5.QtWidgets import QApplication from PyQt5.QtCore import Qt -from PyQt5.QtGui import QColor, QBrush +from PyQt5.QtGui import QColor, QBrush, QPen from pyefis.instruments import gauges import pyavtools.fix as fix import pyefis.hmi as hmi @@ -18,12 +18,18 @@ def app(qtbot): def test_arc_gauge(qtbot): widget = gauges.ArcGauge() assert widget.getRatio() == 2 - widget.setDbkey("NUMOK") + + # Test with no aux data first + widget.setDbkey("NUM") widget.setupGauge() qtbot.addWidget(widget) - widget.resize(201, 200) + widget.resize(300, 200) widget.show() qtbot.waitExposed(widget) + widget.paintEvent(None) + assert widget._dbkey == "NUM" + assert widget.isVisible() + assert widget._units == "°C" widget.resize(200, 220) widget.resizeEvent(None) assert widget.tlcx == 0 @@ -32,7 +38,80 @@ def test_arc_gauge(qtbot): widget.resizeEvent(None) assert widget.tlcx == 100 assert widget.tlcy == 00 + assert int(widget.valueFontSize) == 33 + assert int(widget.unitsFontSize) == 25 + assert int(widget.nameFontSize) == 17 + + widget.font_mask = "00" + widget.units_font_mask = "00" + widget.name_font_mask = "0000" + widget.resizeEvent(None) + assert widget.valueFontSize < 0.5 + assert widget.unitsFontSize < 0.4 + assert widget.nameFontSize < 10 + assert widget.nameFontSize > 9 + widget.name_location = 'right' + widget.resizeEvent(None) + assert widget.nameFontSize < 0.4 + # Switch to NUOK with aux data to check the bar colors + widget.setDbkey("NUMOK") + widget.setupGauge() + widget.paintEvent(None) + + # Many of these I'm not seeing what I could check with assert. + # The paint event paints things based on settings + # no global variables are set to check + # segemnts + + #with mock.patch("PyQt5.QtGui.QPen") as patch_mock: + widget.segments = 28 + widget.paintEvent(None) + #patch_mock.setWidth.assert_called_once() + #patch_mock.setColor.assert_called_once_with(Qt.black) + + widget.name_font_ghost_mask = "0000" + widget.paintEvent(None) + + widget.name_location = 'top' + widget.paintEvent(None) + + widget.name_location = 'right' + widget.name_font_mask = None + widget.paintEvent(None) + + widget.show_units = True + widget.paintEvent(None) + + widget.units_font_mask = None + widget.paintEvent(None) + + widget.units_font_ghost_mask = "0000" + widget.paintEvent(None) + + widget.font_ghost_mask = "0000" + widget.paintEvent(None) + + return + widget.setDbkey("NUMOK") + widget.setupGauge() + widget.resize(201, 200) widget.font_mask = "0000" + widget.units_font_mask = "00" + widget.name_font_mask = "0000" + widget.paintEvent(None) + widget.name_font_ghost_mask = "0000" + widget.paintEvent(None) + widget.resize(300, 100) + widget.name_location = 'right' + widget.resize(301,100) + widget.segments = 28 + widget.paintEvent(None) + widget.name_font_mask = None + widget.paintEvent(None) + widget.setAuxData({"lowWarn": None}) + widget.paintEvent(None) + #widget.setValue(15) + #fix.db.set_value("NUMOK", 15.00) qtbot.wait(5000) From d170d97f4f1adff60ec766b62415a8a081cc944d Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Fri, 14 Jun 2024 15:45:56 -0400 Subject: [PATCH 52/60] Added utility function to tests that can wrap functions and track what arguments are passed to it. Then we can assert that they were called as expected --- tests/instruments/gauges/test_arc.py | 11 ++++++++--- tests/utils.py | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 tests/utils.py diff --git a/tests/instruments/gauges/test_arc.py b/tests/instruments/gauges/test_arc.py index 9084be87..4624fbc9 100644 --- a/tests/instruments/gauges/test_arc.py +++ b/tests/instruments/gauges/test_arc.py @@ -2,11 +2,11 @@ from unittest import mock from PyQt5.QtWidgets import QApplication from PyQt5.QtCore import Qt -from PyQt5.QtGui import QColor, QBrush, QPen +from PyQt5.QtGui import QColor, QBrush, QPen, QPaintEvent from pyefis.instruments import gauges import pyavtools.fix as fix import pyefis.hmi as hmi - +from tests.utils import track_calls @pytest.fixture def app(qtbot): @@ -65,10 +65,15 @@ def test_arc_gauge(qtbot): #with mock.patch("PyQt5.QtGui.QPen") as patch_mock: widget.segments = 28 - widget.paintEvent(None) + #widget.paintEvent(None) #patch_mock.setWidth.assert_called_once() #patch_mock.setColor.assert_called_once_with(Qt.black) + event = QPaintEvent(widget.rect()) + with track_calls(QPen, 'setColor') as tracker: + widget.paintEvent(event) + assert tracker.was_called_with('setColor', QColor(Qt.black)) + assert tracker.was_called_with('setColor', QColor(0, 0, 0, widget.segment_alpha)) widget.name_font_ghost_mask = "0000" widget.paintEvent(None) diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 00000000..9b5b412e --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,25 @@ +from contextlib import contextmanager + +# Used in tests to wrap a function and verify +# it was calld with specific arguments or not +class CallTracker: + def __init__(self): + self.calls = [] + + def track(self, obj, method_name): + method = getattr(obj, method_name) + def wrapper(obj, *args, **kwargs): + self.calls.append((method_name, args, kwargs)) + return method(obj, *args, **kwargs) + + setattr(obj, method_name, wrapper) + + def was_called_with(self, method_name, *args, **kwargs): + return any(call[0] == method_name and call[1] == args for call in self.calls) + +@contextmanager +def track_calls(obj, method_name): + tracker = CallTracker() + tracker.track(obj, method_name) + yield tracker + From 6f1360adc5aca38969232377811ab3896656bc54 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Fri, 14 Jun 2024 16:36:30 -0400 Subject: [PATCH 53/60] Finished arc gauge tests --- conftest.py | 2 + tests/instruments/gauges/test_arc.py | 85 +++++++++++----------------- 2 files changed, 35 insertions(+), 52 deletions(-) diff --git a/conftest.py b/conftest.py index dcac4e42..fdc8f279 100644 --- a/conftest.py +++ b/conftest.py @@ -58,6 +58,8 @@ def create_numbers(key, value, old=False, bad=False, fail=False, annunciate=Fals create_numbers("NUMHIGHWARN", 81.28) create_numbers("NUMHIGHALARM", 95.19) +# Number with no Aux +fix.db.define_item("NUM", "NUM", "float", 0.0, 100.0, "degC", 50000, "") fix.db.define_item("GS", "GS", "float", 0.0, 2000.0, "knots", 50000, "") fix.db.set_value("GS", 100) diff --git a/tests/instruments/gauges/test_arc.py b/tests/instruments/gauges/test_arc.py index 4624fbc9..fe089fee 100644 --- a/tests/instruments/gauges/test_arc.py +++ b/tests/instruments/gauges/test_arc.py @@ -2,12 +2,13 @@ from unittest import mock from PyQt5.QtWidgets import QApplication from PyQt5.QtCore import Qt -from PyQt5.QtGui import QColor, QBrush, QPen, QPaintEvent +from PyQt5.QtGui import QColor, QBrush, QPen, QPaintEvent, QFontMetrics from pyefis.instruments import gauges import pyavtools.fix as fix import pyefis.hmi as hmi from tests.utils import track_calls + @pytest.fixture def app(qtbot): test_app = QApplication.instance() @@ -15,6 +16,7 @@ def app(qtbot): test_app = QApplication([]) return test_app + def test_arc_gauge(qtbot): widget = gauges.ArcGauge() assert widget.getRatio() == 2 @@ -50,7 +52,7 @@ def test_arc_gauge(qtbot): assert widget.unitsFontSize < 0.4 assert widget.nameFontSize < 10 assert widget.nameFontSize > 9 - widget.name_location = 'right' + widget.name_location = "right" widget.resizeEvent(None) assert widget.nameFontSize < 0.4 # Switch to NUOK with aux data to check the bar colors @@ -58,65 +60,44 @@ def test_arc_gauge(qtbot): widget.setupGauge() widget.paintEvent(None) - # Many of these I'm not seeing what I could check with assert. - # The paint event paints things based on settings - # no global variables are set to check - # segemnts - - #with mock.patch("PyQt5.QtGui.QPen") as patch_mock: widget.segments = 28 - #widget.paintEvent(None) - #patch_mock.setWidth.assert_called_once() - #patch_mock.setColor.assert_called_once_with(Qt.black) - event = QPaintEvent(widget.rect()) - with track_calls(QPen, 'setColor') as tracker: - widget.paintEvent(event) + with track_calls(QPen, "setColor") as tracker: + widget.paintEvent(None) + + assert tracker.was_called_with("setColor", QColor(Qt.black)) + assert tracker.was_called_with("setColor", QColor(0, 0, 0, widget.segment_alpha)) - assert tracker.was_called_with('setColor', QColor(Qt.black)) - assert tracker.was_called_with('setColor', QColor(0, 0, 0, widget.segment_alpha)) widget.name_font_ghost_mask = "0000" - widget.paintEvent(None) + with track_calls(QFontMetrics, "width") as tracker: + with track_calls(QColor, "setAlpha") as tracker2: + widget.paintEvent(None) + + assert tracker.was_called_with("width", "0000") + assert tracker2.was_called_with("setAlpha", widget.font_ghost_alpha) - widget.name_location = 'top' + widget.name_location = "top" widget.paintEvent(None) - widget.name_location = 'right' + widget.name_location = "right" widget.name_font_mask = None widget.paintEvent(None) widget.show_units = True widget.paintEvent(None) - widget.units_font_mask = None - widget.paintEvent(None) - - widget.units_font_ghost_mask = "0000" - widget.paintEvent(None) - - widget.font_ghost_mask = "0000" - widget.paintEvent(None) - - return - widget.setDbkey("NUMOK") - widget.setupGauge() - widget.resize(201, 200) - widget.font_mask = "0000" - widget.units_font_mask = "00" - widget.name_font_mask = "0000" - widget.paintEvent(None) - widget.name_font_ghost_mask = "0000" - widget.paintEvent(None) - widget.resize(300, 100) - widget.name_location = 'right' - widget.resize(301,100) - widget.segments = 28 - widget.paintEvent(None) - widget.name_font_mask = None - widget.paintEvent(None) - widget.setAuxData({"lowWarn": None}) - widget.paintEvent(None) - #widget.setValue(15) - #fix.db.set_value("NUMOK", 15.00) - - qtbot.wait(5000) - + # widget.units_font_mask = None + with track_calls(QFontMetrics, "width") as tracker: + widget.paintEvent(None) + assert tracker.was_called_with("width", widget.units_font_mask) + widget.units_font_mask = None + widget.paintEvent(None) + assert tracker.was_called_with("width", widget.units) + + with track_calls(QColor, "setAlpha") as tracker2: + widget.units_font_ghost_mask = "0000" + widget.paintEvent(None) + assert tracker2.was_called_with("setAlpha", widget.font_ghost_alpha) + widget.units_font_ghost_mask = None + widget.font_ghost_mask = "0000" + widget.paintEvent(None) + assert tracker2.was_called_with("setAlpha", widget.font_ghost_alpha) From 06f4905de209577a37649047a6b72a76dc181e16 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Fri, 14 Jun 2024 16:38:26 -0400 Subject: [PATCH 54/60] Fixed wrong type in arc gauge --- src/pyefis/instruments/gauges/arc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pyefis/instruments/gauges/arc.py b/src/pyefis/instruments/gauges/arc.py index b54d6841..6a0f1668 100644 --- a/src/pyefis/instruments/gauges/arc.py +++ b/src/pyefis/instruments/gauges/arc.py @@ -195,7 +195,7 @@ def paintEvent(self, e): if self.name_font_mask: f.setPointSizeF(self.nameFontSize) else: - f.setPixelSize(self.nameFontSize) + f.setPixelSize(int(self.nameFontSize)) fm = QFontMetrics(f) if self.name_font_mask: if self.name_font_ghost_mask: From e4a9cd6a5ff0f3972018a27f9713649ac3dcf921 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Fri, 14 Jun 2024 17:06:08 -0400 Subject: [PATCH 55/60] Added tests for gauges.numeric --- tests/instruments/gauges/test_numeric.py | 44 ++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 tests/instruments/gauges/test_numeric.py diff --git a/tests/instruments/gauges/test_numeric.py b/tests/instruments/gauges/test_numeric.py new file mode 100644 index 00000000..532f4149 --- /dev/null +++ b/tests/instruments/gauges/test_numeric.py @@ -0,0 +1,44 @@ +import pytest +from unittest import mock +from PyQt5.QtWidgets import QApplication +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QColor, QBrush, QPen, QPaintEvent, QFontMetrics +from pyefis.instruments.gauges import numeric +import pyavtools.fix as fix +import pyefis.hmi as hmi +from tests.utils import track_calls + + +@pytest.fixture +def app(qtbot): + test_app = QApplication.instance() + if test_app is None: + test_app = QApplication([]) + return test_app + + +def test_numeric_gauge(qtbot): + widget = numeric.NumericDisplay() + widget.setDbkey("NUM") + widget.setupGauge() + qtbot.addWidget(widget) + widget.resize(300, 200) + widget.show() + qtbot.waitExposed(widget) + widget.resizeEvent(None) + assert widget.font_size == 200 + widget.font_mask = "0000" + widget.resizeEvent(None) + assert widget.font_size != 200 + oldvalueTextRect = widget.valueTextRect + widget.show_units = True + widget.resizeEvent(None) + assert oldvalueTextRect != widget.valueTextRect + widget.font_ghost_mask = "0000" + with track_calls(QColor, "setAlpha") as tracker: + widget.paintEvent(None) + assert tracker.was_called_with("setAlpha", widget.font_ghost_alpha) + widget.font_ghost_mask = None + widget.units_font_ghost_mask = "0000" + widget.paintEvent(None) + assert tracker.was_called_with("setAlpha", widget.font_ghost_alpha) From 6cb46c3336cf4e295f5d6091967acd547a9e9b59 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Fri, 14 Jun 2024 17:53:17 -0400 Subject: [PATCH 56/60] Added more compare options to the tracker utility --- tests/utils.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/utils.py b/tests/utils.py index 9b5b412e..6f6e23ab 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -13,13 +13,23 @@ def wrapper(obj, *args, **kwargs): return method(obj, *args, **kwargs) setattr(obj, method_name, wrapper) + def was_called(self, method_name): + return any( call[0] == method_name for call in self.calls) + + def was_not_called(self, method_name): + return not any( call[0] == method_name for call in self.calls) def was_called_with(self, method_name, *args, **kwargs): return any(call[0] == method_name and call[1] == args for call in self.calls) + def was_not_called_with(self, method_name, *args, **kwargs): + return not any(call[0] == method_name and call[1] == args for call in self.calls) @contextmanager def track_calls(obj, method_name): + if not isinstance(method_name, list): + method_name = [method_name] tracker = CallTracker() - tracker.track(obj, method_name) + for m in method_name: + tracker.track(obj, m) yield tracker From 3bf05ed32602936a44611f5ff58ec404bd0d9a1c Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Fri, 14 Jun 2024 17:54:26 -0400 Subject: [PATCH 57/60] Added tests for horizontal bar gauge --- .../instruments/gauges/test_horizontalBar.py | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 tests/instruments/gauges/test_horizontalBar.py diff --git a/tests/instruments/gauges/test_horizontalBar.py b/tests/instruments/gauges/test_horizontalBar.py new file mode 100644 index 00000000..e5b35c8c --- /dev/null +++ b/tests/instruments/gauges/test_horizontalBar.py @@ -0,0 +1,72 @@ +import pytest +from unittest import mock +from PyQt5.QtWidgets import QApplication +from PyQt5.QtCore import Qt, qRound +from PyQt5.QtGui import QColor, QBrush, QPen, QFont, QPaintEvent, QFontMetrics +from pyefis.instruments import gauges +import pyavtools.fix as fix +import pyefis.hmi as hmi +from tests.utils import track_calls + + +@pytest.fixture +def app(qtbot): + test_app = QApplication.instance() + if test_app is None: + test_app = QApplication([]) + return test_app + + +def test_horizontal_bar_gauge(qtbot): + widget = gauges.HorizontalBar() + assert widget.getRatio() == 2 + widget.setDbkey("NUM") + widget.setupGauge() + qtbot.addWidget(widget) + widget.resize(300, 200) + widget.show() + qtbot.waitExposed(widget) + widget.resizeEvent(None) + with track_calls(QFont, ["setPointSizeF", "setPixelSize"]) as tracker: + widget.resizeEvent(None) + assert tracker.was_not_called("setPointSizeF") + widget.font_mask = "0000" + widget.resizeEvent(None) + assert tracker.was_called("setPointSizeF") + widget.name_font_mask = "0000" + widget.resizeEvent(None) + widget.units_font_mask = "0000" + widget.resizeEvent(None) + + with track_calls(QColor, "setAlpha") as tracker: + widget.name_font_ghost_mask = "0000" + widget.paintEvent(None) + assert tracker.was_called_with("setAlpha", widget.font_ghost_alpha) + + widget.show_units = True + widget.name_font_ghost_mask = None + widget.name_font_mask = None + widget.font_mask = None + with track_calls(QColor, "setAlpha") as tracker: + widget.paintEvent(None) + assert tracker.was_not_called("setAlpha") + widget.units_font_ghost_mask = "0000" + widget.paintEvent(None) + assert tracker.was_called_with("setAlpha", widget.font_ghost_alpha) + + widget.units_font_ghost_mask = None + with track_calls(QColor, "setAlpha") as tracker: + widget.paintEvent(None) + assert tracker.was_not_called("setAlpha") + widget.font_ghost_mask = "0000" + widget.paintEvent(None) + assert tracker.was_called_with("setAlpha", widget.font_ghost_alpha) + widget.setDbkey("NUMOK") + widget.setupGauge() + widget.paintEvent(None) + + widget.segments = 28 + with track_calls(QPen, "setColor") as tracker: + widget.paintEvent(None) + + assert tracker.was_called_with("setColor", QColor(Qt.black)) From 68e91e51fe34633e9d72d9ed051e03f02e16dd86 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Fri, 14 Jun 2024 18:46:34 -0400 Subject: [PATCH 58/60] added test for vertical bar gauge --- tests/instruments/gauges/test_verticalBar.py | 124 +++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 tests/instruments/gauges/test_verticalBar.py diff --git a/tests/instruments/gauges/test_verticalBar.py b/tests/instruments/gauges/test_verticalBar.py new file mode 100644 index 00000000..5485128e --- /dev/null +++ b/tests/instruments/gauges/test_verticalBar.py @@ -0,0 +1,124 @@ +import pytest +from unittest import mock +from PyQt5.QtWidgets import QApplication +from PyQt5.QtCore import Qt, qRound +from PyQt5.QtGui import QColor, QBrush, QPen, QFont, QPaintEvent, QFontMetrics +from pyefis.instruments import gauges +import pyavtools.fix as fix +import pyefis.hmi as hmi +from tests.utils import track_calls + + +@pytest.fixture +def app(qtbot): + test_app = QApplication.instance() + if test_app is None: + test_app = QApplication([]) + return test_app + + +def test_vertical_bar_gauge(qtbot): + widget = gauges.VerticalBar() + assert widget.getRatio() == 0.35 + widget.setDbkey("NUM") + widget.setupGauge() + qtbot.addWidget(widget) + widget.resize(300, 200) + widget.show() + qtbot.waitExposed(widget) + widget.resizeEvent(None) + with track_calls(QFont, ["setPointSizeF", "setPixelSize"]) as tracker: + widget.resizeEvent(None) + assert tracker.was_not_called("setPointSizeF") + widget.font_mask = "0000" + widget.resizeEvent(None) + assert tracker.was_called("setPointSizeF") + widget.name_font_mask = "0000" + widget.resizeEvent(None) + widget.units_font_mask = "0000" + widget.resizeEvent(None) + with track_calls(QColor, "setAlpha") as tracker: + widget.name_font_ghost_mask = "0000" + widget.paintEvent(None) + assert tracker.was_called_with("setAlpha", widget.font_ghost_alpha) + widget.show_units = True + widget.name_font_ghost_mask = None + widget.name_font_mask = None + widget.font_mask = None + with track_calls(QColor, "setAlpha") as tracker: + widget.paintEvent(None) + assert tracker.was_not_called("setAlpha") + widget.units_font_ghost_mask = "0000" + widget.paintEvent(None) + assert tracker.was_called_with("setAlpha", widget.font_ghost_alpha) + widget.units_font_ghost_mask = None + with track_calls(QColor, "setAlpha") as tracker: + widget.paintEvent(None) + assert tracker.was_not_called("setAlpha") + widget.font_ghost_mask = "0000" + widget.paintEvent(None) + assert tracker.was_called_with("setAlpha", widget.font_ghost_alpha) + widget.setDbkey("NUMOK") + widget.setupGauge() + widget.paintEvent(None) + widget.segments = 28 + with track_calls(QPen, "setColor") as tracker: + widget.paintEvent(None) + assert tracker.was_called_with("setColor", QColor(Qt.black)) + widget.setNormalizeMode(True) + assert widget._normalizeMode == True + assert widget.penGoodColor == widget.normalizePenColor + widget.setNormalizeMode(True) + assert widget.penGoodColor == widget.normalizePenColor + widget.setNormalizeMode(False) + assert widget._normalizeMode == False + widget.setPeakMode(True) + assert widget._peakMode == True + widget.setPeakMode(False) + assert widget._peakMode == False + mode = widget._normalizeMode + widget.setMode("normalize") + assert widget.normalizeMode != mode + widget.setMode("normalize") + assert widget.normalizeMode == mode + mode = widget._peakMode + widget.setMode("peak") + assert widget.peakMode != mode + widget.setMode("peak") + assert widget.peakMode == mode + widget.setMode("reset peak") + assert widget.peakValue == widget.value + widget.setMode("lean") + assert widget.peakValue == widget.value + assert widget.normalizeMode == True + assert widget.peakMode == True + widget.setMode("normal") + assert widget.normalizeMode == False + assert widget.peakMode == False + assert widget.barTop != 1 + widget.show_name = False + widget.resizeEvent(None) + assert widget.barTop == 1 + widget.highlight_key = "NUM" + widget.setupGauge() + widget.paintEvent(None) + assert widget.highlight == False + widget.highlight_key = "NUMOK" + widget.setupGauge() + widget.paintEvent(None) + assert widget.highlight == True + widget.setPeakMode(True) + widget.peakValue = 200 + with track_calls(QPen, "setColor") as tracker: + widget.paintEvent(None) + assert tracker.was_called_with("setColor", widget.peakColor) + with track_calls(QPen, "setColor") as tracker: + widget.peakValue = widget.value + widget.paintEvent(None) + assert tracker.was_not_called_with("setColor", widget.peakColor) + widget.units_font_mask = None + widget.paintEvent(None) + widget.setMode("normalize") + widget.paintEvent(None) + widget.normalize_range = 400 + widget.paintEvent(None) From c342e92b7eb7b7b3e07750ff01bab60b62260020 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Fri, 14 Jun 2024 19:59:28 -0400 Subject: [PATCH 59/60] Changed the fix db in conftest.py into a fix fixture to ensure each test can start with a clean database unmodified by other tests --- conftest.py | 154 +++++++++--------- tests/instruments/airspeed/test_airspeed.py | 42 +++-- tests/instruments/gauges/test_arc.py | 3 +- .../instruments/gauges/test_horizontalBar.py | 3 +- tests/instruments/gauges/test_numeric.py | 3 +- tests/instruments/gauges/test_verticalBar.py | 3 +- tests/instruments/misc/test_misc.py | 16 +- tests/instruments/test_numerical_display.py | 1 - 8 files changed, 123 insertions(+), 102 deletions(-) diff --git a/conftest.py b/conftest.py index fdc8f279..1e2b84a4 100644 --- a/conftest.py +++ b/conftest.py @@ -1,3 +1,4 @@ +import pytest # Patch pyavtools.fix so it can run in memory without a fix server import sys import tests.mock_db.client @@ -6,86 +7,93 @@ sys.modules["pyavtools.fix.client"] = tests.mock_db.client sys.modules["pyavtools.scheduler"] = tests.mock_db.scheduler -# import fix -import pyavtools.fix as fix -# Init database -fix.initialize({"main": {"FixServer": "localhost", "FixPort": "3490"}}) -# TODO Add example setting aux values since needed on many tests -# Define some fix db items we can use in tests -# Define a key: -# key,desc, dtype(as a string), min, max, units, tol, aux -fix.db.define_item("TEST", "Testing key", "float", 0, 100, "", 50000, "") -# Set initial Value -fix.db.set_value("TEST", 50.19) -# Bad and fail are true by default to set them False -fix.db.get_item("TEST").bad = False -fix.db.get_item("TEST").fail = False +@pytest.fixture +def fix(): + # import fix + import pyavtools.fix as fix + # Init database + fix.initialize({"main": {"FixServer": "localhost", "FixPort": "3490"}}) + + # TODO Add example setting aux values since needed on many tests + # Define some fix db items we can use in tests + # Define a key: + # key,desc, dtype(as a string), min, max, units, tol, aux + fix.db.define_item("TEST", "Testing key", "float", 0, 100, "", 50000, "") + # Set initial Value + fix.db.set_value("TEST", 50.19) + # Bad and fail are true by default to set them False + fix.db.get_item("TEST").bad = False + fix.db.get_item("TEST").fail = False + + + def create_numbers(key, value, old=False, bad=False, fail=False, annunciate=False): + fix.db.define_item( + key, + "Number", + "float", + 0, + 100, + "degC", + 50000, + "Min,Max,highWarn,highAlarm,lowWarn,lowAlarm", + ) + fix.db.set_value(key, value) + fix.db.get_item(key).old = old + fix.db.get_item(key).bad = bad + fix.db.get_item(key).fail = fail + fix.db.get_item(key).annunciate = annunciate + fix.db.get_item(key).set_aux_value("Min", 0) + fix.db.get_item(key).set_aux_value("lowAlarm", 10) + fix.db.get_item(key).set_aux_value("lowWarn", 20) + fix.db.get_item(key).set_aux_value("highWarn", 80) + fix.db.get_item(key).set_aux_value("highAlarm", 90) + fix.db.get_item(key).set_aux_value("Max", 100) + + + create_numbers("NUMOK", 50.91) + create_numbers("NUMOLD", 51.82, old=True) + create_numbers("NUMBAD", 48.73, bad=True) + create_numbers("NUMFAIL", 65.64, fail=True) + create_numbers("NUMANNUNCIATE", 62.55, annunciate=True) + create_numbers("NUMLOWWARN", 18.46) + create_numbers("NUMLOWALARM", 5.37) + create_numbers("NUMHIGHWARN", 81.28) + create_numbers("NUMHIGHALARM", 95.19) + + # Number with no Aux + fix.db.define_item("NUM", "NUM", "float", 0.0, 100.0, "degC", 50000, "") + + fix.db.define_item("GS", "GS", "float", 0.0, 2000.0, "knots", 50000, "") + fix.db.set_value("GS", 100) + fix.db.get_item("GS").bad = False + fix.db.get_item("GS").fail = False + + fix.db.define_item("TAS", "TAS", "float", 0.0, 2000.0, "knots", 50000, "") + fix.db.set_value("TAS", 105) + fix.db.get_item("TAS").bad = False + fix.db.get_item("TAS").fail = False -def create_numbers(key, value, old=False, bad=False, fail=False, annunciate=False): fix.db.define_item( - key, + "IAS", "Number", "float", 0, - 100, - "degC", + 2000.0, + "knots", 50000, - "Min,Max,highWarn,highAlarm,lowWarn,lowAlarm", + "Min,Max,V1,V2,Vne,Vfe,Vmc,Va,Vno,Vs,Vs0,Vx,Vy", ) - fix.db.set_value(key, value) - fix.db.get_item(key).old = old - fix.db.get_item(key).bad = bad - fix.db.get_item(key).fail = fail - fix.db.get_item(key).annunciate = annunciate - fix.db.get_item(key).set_aux_value("Min", 0) - fix.db.get_item(key).set_aux_value("lowAlarm", 10) - fix.db.get_item(key).set_aux_value("lowWarn", 20) - fix.db.get_item(key).set_aux_value("highWarn", 80) - fix.db.get_item(key).set_aux_value("highAlarm", 90) - fix.db.get_item(key).set_aux_value("Max", 100) - - -create_numbers("NUMOK", 50.91) -create_numbers("NUMOLD", 51.82, old=True) -create_numbers("NUMBAD", 48.73, bad=True) -create_numbers("NUMFAIL", 65.64, fail=True) -create_numbers("NUMANNUNCIATE", 62.55, annunciate=True) -create_numbers("NUMLOWWARN", 18.46) -create_numbers("NUMLOWALARM", 5.37) -create_numbers("NUMHIGHWARN", 81.28) -create_numbers("NUMHIGHALARM", 95.19) - -# Number with no Aux -fix.db.define_item("NUM", "NUM", "float", 0.0, 100.0, "degC", 50000, "") - -fix.db.define_item("GS", "GS", "float", 0.0, 2000.0, "knots", 50000, "") -fix.db.set_value("GS", 100) -fix.db.get_item("GS").bad = False -fix.db.get_item("GS").fail = False - -fix.db.define_item("TAS", "TAS", "float", 0.0, 2000.0, "knots", 50000, "") -fix.db.set_value("TAS", 105) -fix.db.get_item("TAS").bad = False -fix.db.get_item("TAS").fail = False - -fix.db.define_item( - "IAS", - "Number", - "float", - 0, - 2000.0, - "knots", - 50000, - "Min,Max,V1,V2,Vne,Vfe,Vmc,Va,Vno,Vs,Vs0,Vx,Vy", -) -fix.db.set_value("IAS", "110") -fix.db.get_item("IAS").bad = False -fix.db.get_item("IAS").fail = False -fix.db.get_item("IAS").set_aux_value("Vs", 45.0) -fix.db.get_item("IAS").set_aux_value("Vs0", 40.0) -fix.db.get_item("IAS").set_aux_value("Vno", 125.0) -fix.db.get_item("IAS").set_aux_value("Vne", 140.0) -fix.db.get_item("IAS").set_aux_value("Vfe", 70.0) + fix.db.set_value("IAS", "110") + fix.db.get_item("IAS").bad = False + fix.db.get_item("IAS").fail = False + fix.db.get_item("IAS").set_aux_value("Vs", 45.0) + fix.db.get_item("IAS").set_aux_value("Vs0", 40.0) + fix.db.get_item("IAS").set_aux_value("Vno", 125.0) + fix.db.get_item("IAS").set_aux_value("Vne", 140.0) + fix.db.get_item("IAS").set_aux_value("Vfe", 70.0) + + return fix + diff --git a/tests/instruments/airspeed/test_airspeed.py b/tests/instruments/airspeed/test_airspeed.py index 90cee4ae..9e5965b3 100644 --- a/tests/instruments/airspeed/test_airspeed.py +++ b/tests/instruments/airspeed/test_airspeed.py @@ -2,9 +2,8 @@ from unittest import mock from PyQt5.QtWidgets import QApplication from PyQt5.QtCore import Qt -from PyQt5.QtGui import QColor, QBrush +from PyQt5.QtGui import QColor, QBrush, QPaintEvent from pyefis.instruments import airspeed -import pyavtools.fix as fix import pyefis.hmi as hmi @@ -15,14 +14,33 @@ def app(qtbot): test_app = QApplication([]) return test_app +def test_aux_not_set_for_airspeed(fix, qtbot): + fix.db.get_item("IAS").set_aux_value("Vs", None) + fix.db.get_item("IAS").set_aux_value("Vs0", None) + fix.db.get_item("IAS").set_aux_value("Vno", None) + fix.db.get_item("IAS").set_aux_value("Vne", None) + fix.db.get_item("IAS").set_aux_value("Vfe", None) + widget = airspeed.Airspeed() + assert widget.Vs == 0 + assert widget.Vs0 == 0 + assert widget.Vno == 0 + assert widget.Vne == 200 + assert widget.Vfe == 0 + +def test_aux_not_set_for_airspeed_tape(fix, qtbot): + fix.db.get_item("IAS").set_aux_value("Vs", None) + fix.db.get_item("IAS").set_aux_value("Vs0", None) + fix.db.get_item("IAS").set_aux_value("Vno", None) + fix.db.get_item("IAS").set_aux_value("Vne", None) + fix.db.get_item("IAS").set_aux_value("Vfe", None) + widget = airspeed.Airspeed_Tape() + assert widget.Vs == 0 + assert widget.Vs0 == 0 + assert widget.Vno == 0 + assert widget.Vne == 200 + assert widget.Vfe == 0 -def test_numerical_airspeed(qtbot): - fix.db.get_item("IAS").set_aux_value("Vs", 45.0) - fix.db.get_item("IAS").set_aux_value("Vs0", 40.0) - fix.db.get_item("IAS").set_aux_value("Vno", 125.0) - fix.db.get_item("IAS").set_aux_value("Vne", 140.0) - fix.db.get_item("IAS").set_aux_value("Vfe", 70.0) - fix.db.set_value("IAS", "100") +def test_numerical_airspeed(fix, qtbot): widget = airspeed.Airspeed() assert widget.getRatio() == 1 qtbot.addWidget(widget) @@ -50,6 +68,8 @@ def test_numerical_airspeed_tape(qtbot): widget.redraw() widget.resize(50, 200) widget.show() + event = QPaintEvent(widget.rect()) + widget.paintEvent(event) assert widget.Vs0 == 40 assert widget.getAirspeed() == 20 widget.setAirspeed(40) # redraw() @@ -57,7 +77,7 @@ def test_numerical_airspeed_tape(qtbot): widget.wheelEvent(None) -def test_numerical_airspeed_box(qtbot): +def test_numerical_airspeed_box(fix, qtbot): hmi.initialize({}) widget = airspeed.Airspeed_Box() qtbot.addWidget(widget) @@ -87,4 +107,4 @@ def test_numerical_airspeed_box(qtbot): fix.db.get_item("TAS").bad = True fix.db.set_value("TAS", 102) assert widget.valueText == "" - + widget.paintEvent(None) diff --git a/tests/instruments/gauges/test_arc.py b/tests/instruments/gauges/test_arc.py index fe089fee..6f2b9aa0 100644 --- a/tests/instruments/gauges/test_arc.py +++ b/tests/instruments/gauges/test_arc.py @@ -4,7 +4,6 @@ from PyQt5.QtCore import Qt from PyQt5.QtGui import QColor, QBrush, QPen, QPaintEvent, QFontMetrics from pyefis.instruments import gauges -import pyavtools.fix as fix import pyefis.hmi as hmi from tests.utils import track_calls @@ -17,7 +16,7 @@ def app(qtbot): return test_app -def test_arc_gauge(qtbot): +def test_arc_gauge(fix,qtbot): widget = gauges.ArcGauge() assert widget.getRatio() == 2 diff --git a/tests/instruments/gauges/test_horizontalBar.py b/tests/instruments/gauges/test_horizontalBar.py index e5b35c8c..82d9d7c9 100644 --- a/tests/instruments/gauges/test_horizontalBar.py +++ b/tests/instruments/gauges/test_horizontalBar.py @@ -4,7 +4,6 @@ from PyQt5.QtCore import Qt, qRound from PyQt5.QtGui import QColor, QBrush, QPen, QFont, QPaintEvent, QFontMetrics from pyefis.instruments import gauges -import pyavtools.fix as fix import pyefis.hmi as hmi from tests.utils import track_calls @@ -17,7 +16,7 @@ def app(qtbot): return test_app -def test_horizontal_bar_gauge(qtbot): +def test_horizontal_bar_gauge(fix, qtbot): widget = gauges.HorizontalBar() assert widget.getRatio() == 2 widget.setDbkey("NUM") diff --git a/tests/instruments/gauges/test_numeric.py b/tests/instruments/gauges/test_numeric.py index 532f4149..9042428c 100644 --- a/tests/instruments/gauges/test_numeric.py +++ b/tests/instruments/gauges/test_numeric.py @@ -4,7 +4,6 @@ from PyQt5.QtCore import Qt from PyQt5.QtGui import QColor, QBrush, QPen, QPaintEvent, QFontMetrics from pyefis.instruments.gauges import numeric -import pyavtools.fix as fix import pyefis.hmi as hmi from tests.utils import track_calls @@ -17,7 +16,7 @@ def app(qtbot): return test_app -def test_numeric_gauge(qtbot): +def test_numeric_gauge(fix,qtbot): widget = numeric.NumericDisplay() widget.setDbkey("NUM") widget.setupGauge() diff --git a/tests/instruments/gauges/test_verticalBar.py b/tests/instruments/gauges/test_verticalBar.py index 5485128e..49716078 100644 --- a/tests/instruments/gauges/test_verticalBar.py +++ b/tests/instruments/gauges/test_verticalBar.py @@ -4,7 +4,6 @@ from PyQt5.QtCore import Qt, qRound from PyQt5.QtGui import QColor, QBrush, QPen, QFont, QPaintEvent, QFontMetrics from pyefis.instruments import gauges -import pyavtools.fix as fix import pyefis.hmi as hmi from tests.utils import track_calls @@ -17,7 +16,7 @@ def app(qtbot): return test_app -def test_vertical_bar_gauge(qtbot): +def test_vertical_bar_gauge(fix,qtbot): widget = gauges.VerticalBar() assert widget.getRatio() == 0.35 widget.setDbkey("NUM") diff --git a/tests/instruments/misc/test_misc.py b/tests/instruments/misc/test_misc.py index 65d58681..5b9eaba3 100644 --- a/tests/instruments/misc/test_misc.py +++ b/tests/instruments/misc/test_misc.py @@ -7,7 +7,6 @@ from pyefis.instruments.misc import StaticText, ValueDisplay import os -import pyavtools.fix as fix @pytest.fixture @@ -18,7 +17,7 @@ def app(qtbot): return test_app -def test_save_screenshot(qtbot, request): +def test_save_screenshot(fix, qtbot, request): widget1 = StaticText(text="Testing!") widget1.font_mask = "XXXXXXXX" widget1.color = QColor(Qt.black) @@ -36,7 +35,7 @@ def test_save_screenshot(qtbot, request): ) -def test_static_text_default(qtbot): +def test_static_text_default(fix, qtbot): widget = StaticText(text="Testing!") widget.font_mask = "0.0" widget.font_ghost_mask = "0.0" @@ -53,7 +52,7 @@ def test_static_text_default(qtbot): assert widget.color.alpha() != 50 -def test_static_text_resize(qtbot): +def test_static_text_resize(fix, qtbot): widget = StaticText(text="Test", fontsize=1.0) qtbot.addWidget(widget) @@ -65,7 +64,7 @@ def test_static_text_resize(qtbot): assert widget.height() == 100 -def test_value_display_default(qtbot): +def test_value_display_default(fix, qtbot): widget = ValueDisplay() qtbot.addWidget(widget) @@ -79,7 +78,7 @@ def test_value_display_default(qtbot): widget.show() assert widget.isVisible() -def test_value_display_font_mask(qtbot): +def test_value_display_font_mask(fix, qtbot): widget = ValueDisplay() widget.font_size = 100 widget.font_mask = "0.0" @@ -92,7 +91,7 @@ def test_value_display_font_mask(qtbot): assert widget.isVisible() assert widget.font_size != 100 -def test_value_display_set_value(qtbot): +def test_value_display_set_value(fix, qtbot): widget = ValueDisplay() fix.db.set_value("TEST",42.0) fix.db.get_item("TEST").fail = False @@ -107,7 +106,7 @@ def test_value_display_set_value(qtbot): #@mock.patch("pyefis.instruments.misc.fix") #def test_value_display_flags(mock_fix, qtbot): -def test_value_display_flags( qtbot): +def test_value_display_flags(fix, qtbot): #mock_item = mock.Mock() #mock_item.value = 100.0 #mock_fix.db.get_item.return_value = mock_item @@ -115,7 +114,6 @@ def test_value_display_flags( qtbot): fix.db.set_value("TEST",100) widget.setDbkey("TEST") - qtbot.addWidget(widget) widget.failFlag(True) diff --git a/tests/instruments/test_numerical_display.py b/tests/instruments/test_numerical_display.py index 491a643f..2c4fa560 100644 --- a/tests/instruments/test_numerical_display.py +++ b/tests/instruments/test_numerical_display.py @@ -4,7 +4,6 @@ from PyQt5.QtCore import Qt from PyQt5.QtGui import QColor, QBrush from pyefis.instruments.NumericalDisplay import NumericalDisplay -import pyavtools.fix as fix @pytest.fixture From a836d6a305eea073517ac7ca7b54fde2a905b339 Mon Sep 17 00:00:00 2001 From: Eric Blevins Date: Fri, 14 Jun 2024 20:00:20 -0400 Subject: [PATCH 60/60] Prevent divide by zero in verticalBar gauge when normalized mode is enabled without setting normalize_range first --- src/pyefis/instruments/gauges/verticalBar.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pyefis/instruments/gauges/verticalBar.py b/src/pyefis/instruments/gauges/verticalBar.py index f1707cef..5c1fb12d 100644 --- a/src/pyefis/instruments/gauges/verticalBar.py +++ b/src/pyefis/instruments/gauges/verticalBar.py @@ -290,7 +290,7 @@ def paintEvent(self, event): pen.setWidth(1) p.setPen(pen) p.setBrush(brush) - if self.normalizeMode: + if self.normalizeMode and self.normalize_range > 0: nval = self.peakValue - self.normalizeReference start = self.barTop + self.barHeight / 2 y = start - (nval * self.barHeight / self.normalize_range) @@ -304,7 +304,7 @@ def paintEvent(self, event): brush = QBrush(self.penColor) pen.setWidth(1) p.setBrush(brush) - if self.normalizeMode: + if self.normalizeMode and self.normalize_range > 0: pen.setColor(QColor(Qt.gray)) p.setPen(pen) nval = self._value - self.normalizeReference