From a43a82213d7f2431e5768feb7b5bbe71bc6a2cc8 Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Tue, 24 Jun 2025 14:07:58 +0100 Subject: [PATCH] Improve docs for hardware specific tests --- .../picamera2/test_acquisition.py | 15 +++++-- .../picamera2/test_exposure_time_drift.py | 8 +++- .../picamera2/test_sensor_mode.py | 6 ++- .../picamera2/test_tuning.py | 43 ++++++++++++++++--- 4 files changed, 61 insertions(+), 11 deletions(-) diff --git a/hardware-specific-tests/picamera2/test_acquisition.py b/hardware-specific-tests/picamera2/test_acquisition.py index 2929d83d..6d66cc5e 100644 --- a/hardware-specific-tests/picamera2/test_acquisition.py +++ b/hardware-specific-tests/picamera2/test_acquisition.py @@ -1,11 +1,13 @@ -from labthings_picamera2 import StreamingPiCamera2 -from labthings_fastapi.server import ThingServer -from labthings_fastapi.client import ThingClient from fastapi.testclient import TestClient from PIL import Image import numpy as np from pytest import fixture +from labthings_fastapi.server import ThingServer +from labthings_fastapi.client import ThingClient + +from openflexure_microscope_server.things.camera.picamera import StreamingPiCamera2 + @fixture(scope="module") def client(): @@ -17,10 +19,17 @@ def client(): def test_calibration(client): + """ + Check that full auto calibrate completes without an exception + """ client.full_auto_calibrate() def test_jpeg_and_array(client): + """ + Check that grabbing a jpeg from the stream results in the same size + image as a array capture or a jpeg capture. + """ blob = client.grab_jpeg() mjpeg_frame = Image.open(blob.open()) assert mjpeg_frame diff --git a/hardware-specific-tests/picamera2/test_exposure_time_drift.py b/hardware-specific-tests/picamera2/test_exposure_time_drift.py index ee75d179..d7a24f00 100644 --- a/hardware-specific-tests/picamera2/test_exposure_time_drift.py +++ b/hardware-specific-tests/picamera2/test_exposure_time_drift.py @@ -5,12 +5,18 @@ from fastapi.testclient import TestClient from labthings_fastapi.server import ThingServer from labthings_fastapi.client import ThingClient -from labthings_picamera2.thing import StreamingPiCamera2 + +from openflexure_microscope_server.things.camera.picamera import StreamingPiCamera2 logging.basicConfig(level=logging.DEBUG) def test_exposure_time_drift(): + """ + Capture 10 full resolution images and check that the exposure time remains constant + + This confirms that automatic exposure time adjustment is fully turned off + """ cam = StreamingPiCamera2() server = ThingServer() server.add_thing(cam, "/camera/") diff --git a/hardware-specific-tests/picamera2/test_sensor_mode.py b/hardware-specific-tests/picamera2/test_sensor_mode.py index 91694a89..84f6ce7c 100644 --- a/hardware-specific-tests/picamera2/test_sensor_mode.py +++ b/hardware-specific-tests/picamera2/test_sensor_mode.py @@ -5,12 +5,16 @@ import numpy as np from labthings_fastapi.server import ThingServer from labthings_fastapi.client import ThingClient -from labthings_picamera2.thing import StreamingPiCamera2 + +from openflexure_microscope_server.things.camera.picamera import StreamingPiCamera2 logging.basicConfig(level=logging.DEBUG) def test_sensor_mode(): + """ + Test capturing raw arrays in two different sensor modes + """ cam = StreamingPiCamera2() server = ThingServer() server.add_thing(cam, "/camera/") diff --git a/hardware-specific-tests/picamera2/test_tuning.py b/hardware-specific-tests/picamera2/test_tuning.py index 65ab87f8..96691cc8 100644 --- a/hardware-specific-tests/picamera2/test_tuning.py +++ b/hardware-specific-tests/picamera2/test_tuning.py @@ -1,23 +1,30 @@ +""" +Tests that check that a tuning file can be reloaded +""" + import os -from picamera2 import Picamera2 -from labthings_picamera2 import recalibrate_utils import pytest +from picamera2 import Picamera2 + +from openflexure_microscope_server.things.camera import recalibrate_utils MODEL = Picamera2.global_camera_info()[0]["Model"] -def check_camera_available(): - assert len(Picamera2.global_camera_info()) >= 1 - - def load_default_tuning(): + """ + Return the default tuning file for the connected camera. + """ fname = f"{MODEL}.json" return Picamera2.load_tuning_file(fname) def generate_bad_tuning(): + """ + Return a tuning file with an invalid version number to force an error when loaded. + """ default_tuning = load_default_tuning() bad_tuning = default_tuning.copy() bad_tuning["version"] = 999 @@ -25,6 +32,14 @@ def generate_bad_tuning(): def print_tuning(read_file=False): + """ + Print the path of the default tuning file from the the environment variable. + + :param read_file: Boolean, set true to also print the file contents. + + This is useful for debuging. As PyTest suppresses the printing by default the + -s option is needed when running pylint to see this. + """ key = "LIBCAMERA_RPI_TUNING_FILE" if key in os.environ: print(f"Tuning file environment variable: {os.environ[key]}") @@ -36,6 +51,16 @@ def print_tuning(read_file=False): def _test_bad_tuning_after_good_tuning(configure): + """ + Load the default tuning file into the camera, re-load with a broken tuning file, + check it errors. Finally check the default tuning file will load again afterwards. + + :param configure: Boolean, set true to configure the camera on initial loading + + This test checks that: + recalibrate_utils.recreate_camera_manager() is working as expected. As the default + PiCamera2 behaviour does not expect the tuning file to be reloaded. + """ bad_tuning = generate_bad_tuning() default_tuning = load_default_tuning() print_tuning() @@ -61,6 +86,12 @@ def _test_bad_tuning_after_good_tuning(configure): del cam +# Note: The tests below are marked with +# @pytest.mark.filterwarnings("ignore: Exception ignored") +# as the picamera will throw an exception ignored warning when deleting +# the picamera object. This is expected + + @pytest.mark.filterwarnings("ignore: Exception ignored") def test_bad_tuning_after_good_tuning_noconfigure(): _test_bad_tuning_after_good_tuning(False)