From e180cfac353dd96230817559858d65ee5fb24c28 Mon Sep 17 00:00:00 2001 From: ferbraher Date: Mon, 16 Mar 2026 16:22:19 +0100 Subject: [PATCH 01/16] Removed support for qr code --- .../series20/snap/snapcraft.yaml | 1 - .../series22/snap/snapcraft.yaml | 1 - .../series24/snap/snapcraft.yaml | 1 - providers/base/bin/roundtrip_qr.py | 128 ------------------ providers/base/units/camera/README.rst | 1 - providers/base/units/camera/jobs.pxu | 42 ------ providers/base/units/camera/packaging.pxu | 15 -- providers/base/units/camera/test-plan.pxu | 1 - 8 files changed, 190 deletions(-) delete mode 100755 providers/base/bin/roundtrip_qr.py diff --git a/checkbox-core-snap/series20/snap/snapcraft.yaml b/checkbox-core-snap/series20/snap/snapcraft.yaml index 1aac5aa41a..9f86fd475a 100644 --- a/checkbox-core-snap/series20/snap/snapcraft.yaml +++ b/checkbox-core-snap/series20/snap/snapcraft.yaml @@ -380,7 +380,6 @@ parts: - python3-natsort - python3-pil - python3-psutil - - python3-pyqrcode - python3-serial - python3-yaml - python3-pyscard diff --git a/checkbox-core-snap/series22/snap/snapcraft.yaml b/checkbox-core-snap/series22/snap/snapcraft.yaml index a739623f1d..4941801f01 100644 --- a/checkbox-core-snap/series22/snap/snapcraft.yaml +++ b/checkbox-core-snap/series22/snap/snapcraft.yaml @@ -386,7 +386,6 @@ parts: - python3-natsort - python3-pil - python3-psutil - - python3-pyqrcode - python3-serial - python3-yaml - python3-pyscard diff --git a/checkbox-core-snap/series24/snap/snapcraft.yaml b/checkbox-core-snap/series24/snap/snapcraft.yaml index aae7b6043e..c4c266835c 100644 --- a/checkbox-core-snap/series24/snap/snapcraft.yaml +++ b/checkbox-core-snap/series24/snap/snapcraft.yaml @@ -387,7 +387,6 @@ parts: - python3-natsort - python3-pil - python3-psutil - - python3-pyqrcode - python3-serial - python3-yaml - python3-pyscard diff --git a/providers/base/bin/roundtrip_qr.py b/providers/base/bin/roundtrip_qr.py deleted file mode 100755 index 996c646d31..0000000000 --- a/providers/base/bin/roundtrip_qr.py +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/env python3 -# This file is part of Checkbox. -# -# Copyright 2020 Canonical Ltd. -# Written by: -# Jonathan Cave -# -# Checkbox is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 3, -# as published by the Free Software Foundation. -# -# Checkbox 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 Checkbox. If not, see . - - -import os -import random -import string -import time -import subprocess as sp -import sys - -import pyqrcode -import zbar -from PIL import Image - - -def capture_rpi(name): - import picamera - - file = os.path.join( - os.path.expandvars("$PLAINBOX_SESSION_SHARE"), - "{}_qrcapture.png".format(name), - ) - with picamera.PiCamera() as camera: - time.sleep(2) - camera.capture(file) - return file - - -def capture_webcam(name): - file = os.path.join( - os.path.expandvars("$PLAINBOX_SESSION_SHARE"), - "{}_qrcapture.jpg".format(name), - ) - cmd = ( - "gst-launch-1.0 v4l2src device=/dev/{} num-buffers=1 ! jpegenc !" - "filesink location={}" - ).format(name, file) - try: - sp.check_call(cmd, shell=True) - except (sp.CalledProcessError, OSError) as e: - print(e) - raise SystemExit("ERROR: failed to capture image") - return file - - -def generate_data(): - return "".join(random.choice(string.ascii_letters) for i in range(10)) - - -def generate_qr_code(data): - return pyqrcode.create(data) - - -def display_code(qr): - with open("/dev/tty0", "wb+", buffering=0) as term: - # clear the tty so the qr is always printed at the top of the sceen - term.write(str.encode("\033c")) - # print the qr code - term.write(qr.terminal(quiet_zone=1).encode()) - - -def decode_image(filename): - scanner = zbar.ImageScanner() - scanner.parse_config("enable") - pil = Image.open(filename).convert("L") - width, height = pil.size - raw = pil.tobytes() - image = zbar.Image(width, height, "Y800", raw) - scanner.scan(image) - result = None - for code in image: - result = code.data - del image - if result is None: - raise SystemExit("ERROR: no qrcodes decoded") - return result - - -def main(): - if len(sys.argv) != 2: - raise SystemExit("ERROR: expected a device name") - name = sys.argv[1] - print("Testing device name: {}\n".format(name)) - - test_str = generate_data() - print("Input string: {}".format(test_str), flush=True) - - print("Generating QR code...", flush=True) - qr = generate_qr_code(test_str) - - print("Displaying on screen", flush=True) - display_code(qr) - - print("Capture image of screen", flush=True) - if name == "vchiq": - file = capture_rpi(name) - else: - file = capture_webcam(name) - print("Image {} captured".format(file)) - - print("Decoding image file", flush=True) - result = decode_image(file) - print("Decoded data: {}".format(result)) - - if result != test_str: - raise SystemExit("FAIL: decoded data does not match input") - print("PASS: decoded data and input match") - - -if __name__ == "__main__": - main() diff --git a/providers/base/units/camera/README.rst b/providers/base/units/camera/README.rst index 1bca4007e7..3a299aba0f 100644 --- a/providers/base/units/camera/README.rst +++ b/providers/base/units/camera/README.rst @@ -13,7 +13,6 @@ Jobs - **still** - **multiple-resolution-images-rpi** - **multiple-resolution-images-rpi** -- **roundtrip-qrcode** - **camera-quality**: Computes the quality of the image using the brisque score It depends on python3-opencv and libsvm3. diff --git a/providers/base/units/camera/jobs.pxu b/providers/base/units/camera/jobs.pxu index b984c6f4d3..38692b48e1 100644 --- a/providers/base/units/camera/jobs.pxu +++ b/providers/base/units/camera/jobs.pxu @@ -230,45 +230,3 @@ command: _description: This test will attach one of the images used for the multiple resolution images test. - -unit: template -template-engine: jinja2 -template-resource: device -template-filter: device.category in ('CAPTURE', 'MMAL') and device.name != '' -template-unit: job -plugin: shell -category_id: com.canonical.plainbox::camera -id: camera/roundtrip-qrcode_{{ name }} -template-id: camera/roundtrip-qrcode_name -_summary: Test video output and camera {{ name }} by displaying and reading a QR code -estimated_duration: 5.0 -depends: - {%- if category == 'MMAL' %} - camera/detect-rpi - {%- else %} - camera/detect - {% endif -%} -requires: - {# - If the device that generated this test is MMAL, check that we are on armhf - (libmmal doesn't exist for amd64 or arm64). - See: https://github.com/waveform80/picamera/issues/716#issuecomment-1063878114 - #} - (device.name == '{{name}}' and device.category == 'MMAL' and dpkg.architecture == 'armhf') or (device.name == '{{ name}}' and device.category == 'CAPTURE') - {%- if __on_ubuntucore__ %} - os.release >= '19.1' - {%- else %} - os.release >= '19.1' - package.name == 'python3-zbar' - package.name == 'python3-pyqrcode' - package.name == 'python3-pil' - {% endif -%} -command: - roundtrip_qr.py {{ name }} -_purpose: - Generates a QR code representing a random string of ASCII letters. This is - written to tty1 using ASCII escape codes. Either the PiCamera python module or - a GStreamer pipeline is used to capture an image of the display. An attempt - to decode a QR code in the image is then made and data compared against the - random string. -user: root diff --git a/providers/base/units/camera/packaging.pxu b/providers/base/units/camera/packaging.pxu index d1f83c31d5..0d4720945b 100644 --- a/providers/base/units/camera/packaging.pxu +++ b/providers/base/units/camera/packaging.pxu @@ -1,18 +1,3 @@ -# For camera/roundtrip-qrcode_.* -unit: packaging meta-data -os-id: ubuntu -Depends: python3-zbar - -# For camera/roundtrip-qrcode_.* -unit: packaging meta-data -os-id: ubuntu -Depends: python3-pil - -# For camera/roundtrip-qrcode_.* -unit: packaging meta-data -os-id: ubuntu -Depends: python3-pyqrcode - # For camera/camera-quality_.* unit: packaging meta-data os-id: ubuntu diff --git a/providers/base/units/camera/test-plan.pxu b/providers/base/units/camera/test-plan.pxu index 30f52ddf05..ccd0023795 100644 --- a/providers/base/units/camera/test-plan.pxu +++ b/providers/base/units/camera/test-plan.pxu @@ -121,7 +121,6 @@ include: camera/multiple-resolution-images_.* camera/multiple-resolution-images-rpi_.* camera/multiple-resolution-images-rpi-attachment_.* - camera/roundtrip-qrcode_.* bootstrap_include: device From f4d65257344897d6214f83203c48a9359aeb133c Mon Sep 17 00:00:00 2001 From: ferbraher Date: Mon, 16 Mar 2026 16:36:08 +0100 Subject: [PATCH 02/16] Add opencv support for core20 --- checkbox-core-snap/series20/snap/snapcraft.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/checkbox-core-snap/series20/snap/snapcraft.yaml b/checkbox-core-snap/series20/snap/snapcraft.yaml index 9f86fd475a..9ed25b6c3e 100644 --- a/checkbox-core-snap/series20/snap/snapcraft.yaml +++ b/checkbox-core-snap/series20/snap/snapcraft.yaml @@ -361,6 +361,7 @@ parts: - libcap2-bin - libfdt1 - libglu1-mesa + - libsvm3 - lsb-release - lshw - mesa-utils @@ -378,6 +379,7 @@ parts: - python3-evdev - python3-gi - python3-natsort + - python3-opencv - python3-pil - python3-psutil - python3-serial From be52089666d3728d1c03b623e9ef1bb6b7384b61 Mon Sep 17 00:00:00 2001 From: ferbraher Date: Mon, 16 Mar 2026 16:51:02 +0100 Subject: [PATCH 03/16] Update rpi camera tests to include the device name --- providers/base/bin/camera_test_rpi.py | 6 +++--- providers/base/units/camera/jobs.pxu | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/providers/base/bin/camera_test_rpi.py b/providers/base/bin/camera_test_rpi.py index 57d2741ade..61bc2640f1 100755 --- a/providers/base/bin/camera_test_rpi.py +++ b/providers/base/bin/camera_test_rpi.py @@ -38,7 +38,7 @@ ] -def capture(): +def capture(device): path = os.path.expandvars("$PLAINBOX_SESSION_SHARE") print("Images will be written to:\n{}\n".format(path), flush=True) for mode_no, (res, fr) in enumerate(test_res): @@ -47,7 +47,7 @@ def capture(): time.sleep(2) print("Resolution: {}".format(camera.resolution)) print("Framerate: {}".format(camera.framerate)) - file = "picam_{}.jpg".format(mode_no + 1) + file = "picam_{}_{}.jpg".format(mode_no + 1, device) camera.capture(os.path.join(path, file)) print("Image {} captured\n".format(file)) @@ -57,7 +57,7 @@ def main(): parser.add_argument("--device", default="/dev/vchiq", type=str) args = parser.parse_args() print("Resolutions test on device: {}".format(args.device), flush=True) - return capture() + return capture(args.device) if __name__ == "__main__": diff --git a/providers/base/units/camera/jobs.pxu b/providers/base/units/camera/jobs.pxu index 38692b48e1..dac6122ab0 100644 --- a/providers/base/units/camera/jobs.pxu +++ b/providers/base/units/camera/jobs.pxu @@ -220,13 +220,14 @@ plugin: attachment category_id: com.canonical.plainbox::camera id: camera/multiple-resolution-images-rpi-attachment_{name} template-id: camera/multiple-resolution-images-rpi-attachment_name +flags: also-after-suspend _summary: Attach an image from the multiple resolution images test on rpi estimated_duration: 1s after: camera/multiple-resolution-images-rpi_{name} requires: cpuinfo.platform == 'armv7l' command: - [ -f "$PLAINBOX_SESSION_SHARE"/picam_1.jpg ] && - cat "$PLAINBOX_SESSION_SHARE"/picam_1.jpg + [ -f "$PLAINBOX_SESSION_SHARE"/picam_1_{name}.jpg ] && + cat "$PLAINBOX_SESSION_SHARE"/picam_1_{name}.jpg _description: This test will attach one of the images used for the multiple resolution - images test. + images test on rpi. From c2ee69928158887b18af7f68b5505dc52c833541 Mon Sep 17 00:00:00 2001 From: ferbraher Date: Mon, 16 Mar 2026 16:51:31 +0100 Subject: [PATCH 04/16] Added the quality test for raspis running core >=20 --- providers/base/units/camera/jobs.pxu | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/providers/base/units/camera/jobs.pxu b/providers/base/units/camera/jobs.pxu index dac6122ab0..d98f84c49e 100644 --- a/providers/base/units/camera/jobs.pxu +++ b/providers/base/units/camera/jobs.pxu @@ -231,3 +231,24 @@ command: _description: This test will attach one of the images used for the multiple resolution images test on rpi. + +unit: template +template-resource: device +template-filter: device.category == 'MMAL' and device.name != '' +template-unit: job +plugin: shell +category_id: com.canonical.plainbox::camera +id: camera/camera-quality-rpi_{name} +template-id: camera/camera-quality-rpi_name +flags: also-after-suspend +_summary: Webcam BRISQUE score for rpi devices on {product_slug} +estimated_duration: 20s +depends: camera/detect +after: camera/multiple-resolution-images-rpi_{name} +requires: os.release >= '20' +command: + camera_quality_test.py -f "$PLAINBOX_SESSION_SHARE"/picam_1_{name}.jpg +_purpose: + Uses an image of the camera to get the quality based on a No-Reference image + quality assessment algorithm called BRISQUE. This test will timeout and fail + if the quality has not been computed within 120 seconds. From 3d053fd901f2fe445d64b23df3eec2a61717bf63 Mon Sep 17 00:00:00 2001 From: ferbraher Date: Tue, 17 Mar 2026 16:41:19 +0100 Subject: [PATCH 05/16] Removed after suspend flag and fixed wrong detect test --- providers/base/units/camera/jobs.pxu | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/providers/base/units/camera/jobs.pxu b/providers/base/units/camera/jobs.pxu index d98f84c49e..518b74ce93 100644 --- a/providers/base/units/camera/jobs.pxu +++ b/providers/base/units/camera/jobs.pxu @@ -220,7 +220,6 @@ plugin: attachment category_id: com.canonical.plainbox::camera id: camera/multiple-resolution-images-rpi-attachment_{name} template-id: camera/multiple-resolution-images-rpi-attachment_name -flags: also-after-suspend _summary: Attach an image from the multiple resolution images test on rpi estimated_duration: 1s after: camera/multiple-resolution-images-rpi_{name} @@ -240,10 +239,9 @@ plugin: shell category_id: com.canonical.plainbox::camera id: camera/camera-quality-rpi_{name} template-id: camera/camera-quality-rpi_name -flags: also-after-suspend _summary: Webcam BRISQUE score for rpi devices on {product_slug} estimated_duration: 20s -depends: camera/detect +depends: camera/detect-rpi after: camera/multiple-resolution-images-rpi_{name} requires: os.release >= '20' command: From 75215bdcfe972e409726eeb32096a933d63c9a8c Mon Sep 17 00:00:00 2001 From: ferbraher Date: Tue, 17 Mar 2026 16:42:58 +0100 Subject: [PATCH 06/16] moved rpi-detec test for easier reading --- providers/base/units/camera/jobs.pxu | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/providers/base/units/camera/jobs.pxu b/providers/base/units/camera/jobs.pxu index 518b74ce93..8f5672a42a 100644 --- a/providers/base/units/camera/jobs.pxu +++ b/providers/base/units/camera/jobs.pxu @@ -11,17 +11,6 @@ command: _summary: This Automated test attempts to detect a camera. user: root -plugin: shell -category_id: com.canonical.plainbox::camera -id: camera/detect-rpi -estimated_duration: 1.0 -imports: from com.canonical.plainbox import manifest -requires: - manifest.has_rpi_camera == 'True' -command: - udev_resource.py -f MMAL | grep "category: MMAL" -_summary: Detect presence of a MMAL camera. - unit: template category_id: com.canonical.plainbox::camera template-resource: device @@ -191,6 +180,17 @@ command: _description: This test will attach the image used for the BRISQUE score. +plugin: shell +category_id: com.canonical.plainbox::camera +id: camera/detect-rpi +estimated_duration: 1.0 +imports: from com.canonical.plainbox import manifest +requires: + manifest.has_rpi_camera == 'True' +command: + udev_resource.py -f MMAL | grep "category: MMAL" +_summary: Detect presence of a MMAL camera. + unit: template template-resource: device template-filter: device.category == 'MMAL' and device.name != '' From 7455a189891882c2902fadbbe1533b6e98e317a2 Mon Sep 17 00:00:00 2001 From: ferbraher Date: Tue, 17 Mar 2026 16:43:30 +0100 Subject: [PATCH 07/16] Added the camera quality tests for core test-plan --- providers/base/units/camera/test-plan.pxu | 1 + 1 file changed, 1 insertion(+) diff --git a/providers/base/units/camera/test-plan.pxu b/providers/base/units/camera/test-plan.pxu index ccd0023795..d7c521e61b 100644 --- a/providers/base/units/camera/test-plan.pxu +++ b/providers/base/units/camera/test-plan.pxu @@ -121,6 +121,7 @@ include: camera/multiple-resolution-images_.* camera/multiple-resolution-images-rpi_.* camera/multiple-resolution-images-rpi-attachment_.* + camera/camera-quality-rpi_name bootstrap_include: device From 015d4be65991322ef74c360110be0988f3746642 Mon Sep 17 00:00:00 2001 From: Fernando Bravo <39527354+fernando79513@users.noreply.github.com> Date: Wed, 18 Mar 2026 12:05:25 +0100 Subject: [PATCH 08/16] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- providers/base/units/camera/jobs.pxu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/base/units/camera/jobs.pxu b/providers/base/units/camera/jobs.pxu index 8f5672a42a..ad25233d89 100644 --- a/providers/base/units/camera/jobs.pxu +++ b/providers/base/units/camera/jobs.pxu @@ -243,7 +243,7 @@ _summary: Webcam BRISQUE score for rpi devices on {product_slug} estimated_duration: 20s depends: camera/detect-rpi after: camera/multiple-resolution-images-rpi_{name} -requires: os.release >= '20' +requires: cpuinfo.platform == 'armv7l' and os.release >= '20' command: camera_quality_test.py -f "$PLAINBOX_SESSION_SHARE"/picam_1_{name}.jpg _purpose: From 1d5cbd1af806e86aa38cf7fd7c39e50c39313536 Mon Sep 17 00:00:00 2001 From: ferbraher Date: Wed, 18 Mar 2026 12:07:34 +0100 Subject: [PATCH 09/16] Updated rst file --- providers/base/units/camera/README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/providers/base/units/camera/README.rst b/providers/base/units/camera/README.rst index 3a299aba0f..8d0cd5c816 100644 --- a/providers/base/units/camera/README.rst +++ b/providers/base/units/camera/README.rst @@ -12,9 +12,10 @@ Jobs - **led** - **still** - **multiple-resolution-images-rpi** -- **multiple-resolution-images-rpi** - **camera-quality**: Computes the quality of the image using the brisque score It depends on python3-opencv and libsvm3. +- **camera-quality-rpi**: RPi-specific variant of the BRISQUE image quality check. +- **roundtrip-qrcode** **NOTE:** @@ -22,4 +23,3 @@ Jobs `LD_LIBRARY_PATH`. If the version is updated in the future, it should be changed manually in the `svm.py` file under `checkbox-support/vendor/brisque/svm`. - From 94040defef945ba5923ccae5f6a037b2ef291256 Mon Sep 17 00:00:00 2001 From: ferbraher Date: Wed, 18 Mar 2026 12:26:41 +0100 Subject: [PATCH 10/16] Fixed wrong product slug field --- providers/base/units/camera/jobs.pxu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/base/units/camera/jobs.pxu b/providers/base/units/camera/jobs.pxu index ad25233d89..ac8a2e0700 100644 --- a/providers/base/units/camera/jobs.pxu +++ b/providers/base/units/camera/jobs.pxu @@ -239,7 +239,7 @@ plugin: shell category_id: com.canonical.plainbox::camera id: camera/camera-quality-rpi_{name} template-id: camera/camera-quality-rpi_name -_summary: Webcam BRISQUE score for rpi devices on {product_slug} +_summary: Webcam BRISQUE score for rpi devices on {name} estimated_duration: 20s depends: camera/detect-rpi after: camera/multiple-resolution-images-rpi_{name} From 83ffdb2ebd9a601b254e922ac8afa1e906f0ad69 Mon Sep 17 00:00:00 2001 From: ferbraher Date: Wed, 18 Mar 2026 12:26:58 +0100 Subject: [PATCH 11/16] Using only the name to create the file --- providers/base/bin/camera_test_rpi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/base/bin/camera_test_rpi.py b/providers/base/bin/camera_test_rpi.py index 61bc2640f1..a714f83e51 100755 --- a/providers/base/bin/camera_test_rpi.py +++ b/providers/base/bin/camera_test_rpi.py @@ -47,7 +47,7 @@ def capture(device): time.sleep(2) print("Resolution: {}".format(camera.resolution)) print("Framerate: {}".format(camera.framerate)) - file = "picam_{}_{}.jpg".format(mode_no + 1, device) + file = "picam_{}_{}.jpg".format(mode_no + 1, device.split("/")[-1]) camera.capture(os.path.join(path, file)) print("Image {} captured\n".format(file)) From d64eedf63ab8eefc5b43dba0f617a33fb6ba9d37 Mon Sep 17 00:00:00 2001 From: ferbraher Date: Wed, 18 Mar 2026 12:39:28 +0100 Subject: [PATCH 12/16] removed qr test from rst --- providers/base/units/camera/README.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/providers/base/units/camera/README.rst b/providers/base/units/camera/README.rst index 8d0cd5c816..c19e466989 100644 --- a/providers/base/units/camera/README.rst +++ b/providers/base/units/camera/README.rst @@ -15,7 +15,6 @@ Jobs - **camera-quality**: Computes the quality of the image using the brisque score It depends on python3-opencv and libsvm3. - **camera-quality-rpi**: RPi-specific variant of the BRISQUE image quality check. -- **roundtrip-qrcode** **NOTE:** From 2ed0767a82a1ad80e81d08a02aa44866654233b4 Mon Sep 17 00:00:00 2001 From: ferbraher Date: Wed, 18 Mar 2026 16:54:47 +0100 Subject: [PATCH 13/16] Added pycamera tests --- providers/base/bin/camera_test_rpi.py | 4 ++ providers/base/tests/test_camera_test_rpi.py | 65 ++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 providers/base/tests/test_camera_test_rpi.py diff --git a/providers/base/bin/camera_test_rpi.py b/providers/base/bin/camera_test_rpi.py index a714f83e51..6277469a7d 100755 --- a/providers/base/bin/camera_test_rpi.py +++ b/providers/base/bin/camera_test_rpi.py @@ -41,12 +41,15 @@ def capture(device): path = os.path.expandvars("$PLAINBOX_SESSION_SHARE") print("Images will be written to:\n{}\n".format(path), flush=True) + for mode_no, (res, fr) in enumerate(test_res): with picamera.PiCamera(resolution=res, framerate=fr) as camera: print("Camera initialised, wait to settle...", flush=True) time.sleep(2) + print("Resolution: {}".format(camera.resolution)) print("Framerate: {}".format(camera.framerate)) + file = "picam_{}_{}.jpg".format(mode_no + 1, device.split("/")[-1]) camera.capture(os.path.join(path, file)) print("Image {} captured\n".format(file)) @@ -56,6 +59,7 @@ def main(): parser = argparse.ArgumentParser(description="PiCamera Tests") parser.add_argument("--device", default="/dev/vchiq", type=str) args = parser.parse_args() + print("Resolutions test on device: {}".format(args.device), flush=True) return capture(args.device) diff --git a/providers/base/tests/test_camera_test_rpi.py b/providers/base/tests/test_camera_test_rpi.py new file mode 100644 index 0000000000..ef4a85f91f --- /dev/null +++ b/providers/base/tests/test_camera_test_rpi.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +# Copyright 2026 Canonical Ltd. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3, +# as published by the Free Software Foundation. +# +# 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, see . + +import sys +import unittest +from unittest.mock import MagicMock, call, patch + +sys.modules["picamera"] = MagicMock() +from camera_test_rpi import main, capture, test_res # noqa: E402 + + +class CameraTestRPITests(unittest.TestCase): + """Test cases for the camera_test_rpi module.""" + + @patch("builtins.print", MagicMock()) + @patch("camera_test_rpi.time.sleep", MagicMock()) + @patch("camera_test_rpi.os.path") + @patch("camera_test_rpi.picamera.PiCamera") + def test_capture(self, mock_picamera, mock_path): + mock_path.expandvars.return_value = "/tmp/session" + mock_path.join.side_effect = [ + "/tmp/session/picam_{}_vchiq.jpg".format(i) + for i in range(1, len(test_res) + 2) + ] + + capture("/dev/vchiq") + + # Verify the camera calls + expected_picamera_calls = [ + call(resolution=res, framerate=fr) for res, fr in test_res + ] + self.assertEqual(mock_picamera.call_count, len(test_res)) + self.assertEqual(mock_picamera.call_args_list, expected_picamera_calls) + + # Verify the capture calls + expected_capture_calls = [ + call("/tmp/session/picam_{}_{}.jpg".format(index, "vchiq")) + for index in range(1, len(test_res) + 1) + ] + camera = mock_picamera.return_value.__enter__.return_value + camera.capture.assert_has_calls(expected_capture_calls) + + @patch("camera_test_rpi.capture") + def test_main_passes_device(self, mock_capture): + sys.argv = ["camera_test_rpi.py", "--device", "/dev/video0"] + main() + mock_capture.assert_called_once_with("/dev/video0") + + @patch("camera_test_rpi.capture") + def test_main_default_device(self, mock_capture): + sys.argv = ["camera_test_rpi.py"] + main() + mock_capture.assert_called_once_with("/dev/vchiq") From d5bfd92dd82626e57b0875451980a0e70b534a08 Mon Sep 17 00:00:00 2001 From: ferbraher Date: Thu, 19 Mar 2026 11:38:07 +0100 Subject: [PATCH 14/16] Updated RST --- providers/base/units/camera/README.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/providers/base/units/camera/README.rst b/providers/base/units/camera/README.rst index c19e466989..7f5bfa311b 100644 --- a/providers/base/units/camera/README.rst +++ b/providers/base/units/camera/README.rst @@ -14,7 +14,9 @@ Jobs - **multiple-resolution-images-rpi** - **camera-quality**: Computes the quality of the image using the brisque score It depends on python3-opencv and libsvm3. -- **camera-quality-rpi**: RPi-specific variant of the BRISQUE image quality check. +- **camera-quality-rpi**: RPi-specific variant of the BRISQUE image quality + check. The algorithm is the same as the one, but the raspberry pi can't + use opencv to grab the image, so we pass the image as an argument. **NOTE:** From 776ffcae27b2e364f994d6a5640302632298da5e Mon Sep 17 00:00:00 2001 From: ferbraher Date: Thu, 19 Mar 2026 11:51:04 +0100 Subject: [PATCH 15/16] Small changes in rst --- providers/base/units/camera/README.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/providers/base/units/camera/README.rst b/providers/base/units/camera/README.rst index 7f5bfa311b..1262960570 100644 --- a/providers/base/units/camera/README.rst +++ b/providers/base/units/camera/README.rst @@ -1,7 +1,7 @@ Camera unit ======================= -Unit is responsible for camera control and image processing tests. +Unit is responsible for camera control and image-processing tests. Jobs #### @@ -12,11 +12,11 @@ Jobs - **led** - **still** - **multiple-resolution-images-rpi** -- **camera-quality**: Computes the quality of the image using the brisque score +- **camera-quality**: Computes the quality of the image using the BRISQUE score It depends on python3-opencv and libsvm3. -- **camera-quality-rpi**: RPi-specific variant of the BRISQUE image quality - check. The algorithm is the same as the one, but the raspberry pi can't - use opencv to grab the image, so we pass the image as an argument. +- **camera-quality-rpi**: RPi-specific variant of the BRISQUE image-quality + check. The algorithm is the same as as in `camera-quality`, but the Raspberry + Pi can't use OpenCV to grab the image, so we pass it as an argument. **NOTE:** From 641fc42154ca4cec475743110e860d9b2f1584e1 Mon Sep 17 00:00:00 2001 From: ferbraher Date: Fri, 20 Mar 2026 13:21:41 +0100 Subject: [PATCH 16/16] Removed custom opencv build from core 22 --- .../series22/snap/snapcraft.yaml | 46 +------------------ 1 file changed, 1 insertion(+), 45 deletions(-) diff --git a/checkbox-core-snap/series22/snap/snapcraft.yaml b/checkbox-core-snap/series22/snap/snapcraft.yaml index 4941801f01..753fa1e3e6 100644 --- a/checkbox-core-snap/series22/snap/snapcraft.yaml +++ b/checkbox-core-snap/series22/snap/snapcraft.yaml @@ -384,6 +384,7 @@ parts: - python3-evdev - python3-gi - python3-natsort + - python3-opencv - python3-pil - python3-psutil - python3-serial @@ -516,51 +517,6 @@ parts: python3 manage.py build python3 manage.py install --layout=relocatable --prefix=/providers/checkbox-provider-tutorial --root="$SNAPCRAFT_PART_INSTALL" after: [checkbox-provider-base] -################################################################################ - opencv: - plugin: make - source: https://github.com/opencv/opencv.git - source-tag: 4.9.0 - override-build: | - cd $SNAPCRAFT_PART_SRC - mkdir build - cd build - cmake -DCMAKE_BUILD_TYPE=RELEASE \ - -DCMAKE_INSTALL_PREFIX=$SNAPCRAFT_PART_INSTALL \ - -DBUILD_LIST=videoio,features2d,highgui,flann,python3 .. - make -j$(nproc) - make install - build-packages: - - build-essential - - cmake - - pkg-config - - libjpeg-dev - - libpng-dev - - libtiff-dev - - libavcodec-dev - - libavformat-dev - - libswscale-dev - - libv4l-dev - - libxvidcore-dev - - libx264-dev - - libgtk-3-dev - - gfortran - - python3-dev - - python3-numpy - stage-packages: - - libjpeg8 - - libpng16-16 - - libtiff5 - - libavcodec58 - - libavformat58 - - libfreetype6 - - libswscale5 - - libv4l-0 - - libxvidcore4 - - libx264-dev - - python3-minimal - - python3.10-minimal - - python3-numpy ################################################################################ gnome-randr: source: https://github.com/maxwellainatchi/gnome-randr-rust.git