diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 982ad6d1..331fb06f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -145,7 +145,7 @@ tests: coverage_format: cobertura path: coverage.xml paths: - - tests/unit_tests/reference_failures/ + - tests/reference_failures/ when: always test-matrix: diff --git a/bec_widgets/utils/reference_utils.py b/bec_widgets/utils/reference_utils.py new file mode 100644 index 00000000..6766a13e --- /dev/null +++ b/bec_widgets/utils/reference_utils.py @@ -0,0 +1,92 @@ +import os +import sys + +from PIL import Image, ImageChops +from qtpy.QtGui import QPixmap + +import bec_widgets + +REFERENCE_DIR = os.path.join( + os.path.dirname(os.path.dirname(bec_widgets.__file__)), "tests/references" +) +REFERENCE_DIR_FAILURES = os.path.join( + os.path.dirname(os.path.dirname(bec_widgets.__file__)), "tests/reference_failures" +) + + +def compare_images(image1_path: str, reference_image_path: str): + """ + Load two images and compare them pixel by pixel + + Args: + image1_path(str): The path to the first image + reference_image_path(str): The path to the reference image + + Raises: + ValueError: If the images are different + """ + image1 = Image.open(image1_path) + image2 = Image.open(reference_image_path) + if image1.size != image2.size: + raise ValueError("Image size has changed") + diff = ImageChops.difference(image1, image2) + if diff.getbbox(): + # copy image1 to the reference directory to upload as artifact + os.makedirs(REFERENCE_DIR_FAILURES, exist_ok=True) + image_name = os.path.join(REFERENCE_DIR_FAILURES, os.path.basename(image1_path)) + image1.save(image_name) + print(f"Image saved to {image_name}") + + raise ValueError("Images are different") + + +def snap_and_compare(widget: any, output_directory: str, suffix: str = ""): + """ + Save a rendering of a widget and compare it to a reference image + + Args: + widget(any): The widget to render + output_directory(str): The directory to save the image to + suffix(str): A suffix to append to the image name + + Raises: + ValueError: If the images are different + + Examples: + snap_and_compare(widget, tmpdir, suffix="started") + + """ + + if not isinstance(output_directory, str): + output_directory = str(output_directory) + + os_suffix = sys.platform + + name = ( + f"{widget.__class__.__name__}_{suffix}_{os_suffix}.png" + if suffix + else f"{widget.__class__.__name__}_{os_suffix}.png" + ) + + # Save the widget to a pixmap + test_image_path = os.path.join(output_directory, name) + pixmap = QPixmap(widget.size()) + widget.render(pixmap) + pixmap.save(test_image_path) + + try: + reference_path = os.path.join(REFERENCE_DIR, f"{widget.__class__.__name__}") + reference_image_path = os.path.join(reference_path, name) + + if not os.path.exists(reference_image_path): + raise ValueError(f"Reference image not found: {reference_image_path}") + + compare_images(test_image_path, reference_image_path) + + except ValueError: + image = Image.open(test_image_path) + os.makedirs(REFERENCE_DIR_FAILURES, exist_ok=True) + image_name = os.path.join(REFERENCE_DIR_FAILURES, name) + image.save(image_name) + print(f"Image saved to {image_name}") + raise diff --git a/tests/unit_tests/references/SpinnerWidget_darwin.png b/tests/references/SpinnerWidget/SpinnerWidget_darwin.png similarity index 100% rename from tests/unit_tests/references/SpinnerWidget_darwin.png rename to tests/references/SpinnerWidget/SpinnerWidget_darwin.png diff --git a/tests/unit_tests/references/SpinnerWidget_linux.png b/tests/references/SpinnerWidget/SpinnerWidget_linux.png similarity index 100% rename from tests/unit_tests/references/SpinnerWidget_linux.png rename to tests/references/SpinnerWidget/SpinnerWidget_linux.png diff --git a/tests/unit_tests/references/SpinnerWidget_started_darwin.png b/tests/references/SpinnerWidget/SpinnerWidget_started_darwin.png similarity index 100% rename from tests/unit_tests/references/SpinnerWidget_started_darwin.png rename to tests/references/SpinnerWidget/SpinnerWidget_started_darwin.png diff --git a/tests/unit_tests/references/SpinnerWidget_started_linux.png b/tests/references/SpinnerWidget/SpinnerWidget_started_linux.png similarity index 100% rename from tests/unit_tests/references/SpinnerWidget_started_linux.png rename to tests/references/SpinnerWidget/SpinnerWidget_started_linux.png diff --git a/tests/unit_tests/test_spinner.py b/tests/unit_tests/test_spinner.py index 8b6a0762..00c787c1 100644 --- a/tests/unit_tests/test_spinner.py +++ b/tests/unit_tests/test_spinner.py @@ -1,11 +1,7 @@ -import os -import sys - import pytest import qdarktheme -from PIL import Image, ImageChops -from qtpy.QtGui import QPixmap +from bec_widgets.utils.reference_utils import snap_and_compare from bec_widgets.widgets.spinner.spinner import SpinnerWidget @@ -18,75 +14,17 @@ def spinner_widget(qtbot): yield spinner -def save_pixmap(widget, filename): - pixmap = QPixmap(widget.size()) - widget.render(pixmap) - pixmap.save(str(filename)) - return pixmap - - -def compare_images(image1_path: str, reference_image_path: str): - image1 = Image.open(image1_path) - image2 = Image.open(reference_image_path) - if image1.size != image2.size: - raise ValueError("Image size has changed") - diff = ImageChops.difference(image1, image2) - if diff.getbbox(): - # copy image1 to the reference directory to upload as artifact - output_dir = os.path.join(os.path.dirname(__file__), "reference_failures") - os.makedirs(output_dir, exist_ok=True) - image_name = os.path.join(output_dir, os.path.basename(image1_path)) - image1.save(image_name) - print(f"Image saved to {image_name}") - - raise ValueError("Images are different") - - def test_spinner_widget_paint_event(spinner_widget, qtbot): spinner_widget.paintEvent(None) -def snap_and_compare(widget, tmpdir, suffix=""): - os_suffix = sys.platform - - name = ( - f"{widget.__class__.__name__}_{suffix}_{os_suffix}.png" - if suffix - else f"{widget.__class__.__name__}_{os_suffix}.png" - ) - - # Save the widget to a pixmap - test_image_path = str(tmpdir / name) - pixmap = QPixmap(widget.size()) - widget.render(pixmap) - pixmap.save(test_image_path) - - try: - references_path = os.path.join(os.path.dirname(__file__), "references") - reference_image_path = os.path.join(references_path, name) - - if not os.path.exists(reference_image_path): - raise ValueError(f"Reference image not found: {reference_image_path}") - - compare_images(test_image_path, reference_image_path) - - except ValueError: - image = Image.open(test_image_path) - output_dir = os.path.join(os.path.dirname(__file__), "reference_failures") - os.makedirs(output_dir, exist_ok=True) - image_name = os.path.join(output_dir, name) - image.save(image_name) - print(f"Image saved to {image_name}") - raise - - def test_spinner_widget_rendered(spinner_widget, qtbot, tmpdir): spinner_widget.update() qtbot.wait(200) - snap_and_compare(spinner_widget, tmpdir, suffix="") + snap_and_compare(spinner_widget, str(tmpdir), suffix="") spinner_widget._started = True spinner_widget.update() qtbot.wait(200) - snap_and_compare(spinner_widget, tmpdir, suffix="started") + snap_and_compare(spinner_widget, str(tmpdir), suffix="started")