From patchwork Fri Oct 16 20:29:10 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ray Strode X-Patchwork-Id: 28485 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id F171EC3305 for ; Fri, 16 Oct 2026 20:29:29 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 9AB9668E57; Fri, 16 Oct 2026 22:29:29 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="sfjoOqdw"; dkim-atps=neutral Received: from mail-ua1-x932.google.com (mail-ua1-x932.google.com [IPv6:2607:f8b0:4864:20::932]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id A3DE668E3D for ; Fri, 16 Oct 2026 22:29:27 +0200 (CEST) Received: by mail-ua1-x932.google.com with SMTP id a1e0cc1a2514c-9906a5410f5so1358680241.0 for ; Fri, 16 Oct 2026 13:29:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792182566; x=1792787366; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=frglHG+FRg5h7LVXbE64TfdmiDFbHBi0FtPryEjADy4=; b=sfjoOqdw68/VtLIS2O56C3usmUIzzzyE0tPEhIcVPQkGRYq+px7zc5iPKqGeXG/l3M 4MEz1lGkwbDQMsvCPTwRI3biB3FPRBy59ki+WaEiEUS90gxegaZfyiiZUAGvH7OcQxqT BGPJsPh/lZtLIlYdt6fhV2scg3x9N33V7FsgEmYlh/fK/CwaDhGPf5+3tjzHbd/3rD9+ GYiriCpDbWlhpRkGt+mwDe37MNEVoKXI8QajXR5Yb6ST+CFmMIo10Ci8s94bh7E0VEog i3ZZ4E/x8Goi/Q2ZL2P/zYR+hi9co5U9XYHsyqFc8KOK0D16lhRpJjcLf2s7agqhw/8S 0q2g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792182566; x=1792787366; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=frglHG+FRg5h7LVXbE64TfdmiDFbHBi0FtPryEjADy4=; b=rbfPmSqRPrzm7rf2gNXTF2tHOySM4cROtADpksJ2PMBYixBbz+x8500KTnaJZ3n19t 8DsKRaVNSOEj+vYr7rFTc+EHmMiKDVWEUycSTmi+GQlDDgyUW6E/rjRKZx0T0rErLzpT IACduZOJwiRKXq3mbGoh0BrHYnXlTook02rA/aNOCPecL+F7+/r8Eaizci521C1LyZQo Mm8dC1PpAVxlLUcjcy5oWtMj6gSPo5xsgHT0uh9GpTDEHE1SZTijInIr/WMbCn3AuTfu RYJodpIbSWDh56t4jXHbAnAiTonmro46p/lnkrr2WEVG9Nlxqhzm9dA6S0XtMq+pfYUk pyfw== X-Gm-Message-State: AFq9FYI1pgamD2UxzqN2QbBPt0Tluy+8mnCNYo3vbZuD2VVORQhhk0WF qG/8Vx3TQ+Xzl8qBcchArVu/LtbzkIIVonEkbv932AToKH5pD63z8PooXmEcWbfVTdc= X-Gm-Gg: AYBFou0jepA7SsAdsTUzjrBoXv/PHGA7pmJt3y7iZn3qumoFkmq3B6q11Iwp3+QMgmV JKWG1n+8NsQW4wpsWcPaF9YaYU8Kj2UBxNtJmoe93zCDHMV0zxtBgfIl3e830RBFUlNYZcA13cg BKS95tSGpMqbP+6xGMzRDjTsN95Y6CU5deEsBsMjPg8SCO2ulWKJqe+uSNzoZgZpR7kYV6xO8iM ZlM3gnXiWZUasCo7wP6PwBGbj2zPVDuUgC9GpIlTZymPEiJeTaVyShS4s8AWYmQTV/4vxj6RMHL ni+C/0AcuWCV6GCAoF/y09Wlae98HnHDbcmSJz9D7eKU5+v6s7liJ+AvcGYrpCZ7bCtA1rp+E0s UHJ3XQCPb6+bkDlFHDbWoJ55U7gkx0EHwkVLAtBF6VfatTt7+48EF233A4vAJHntfwTc6WwBb3I qDrBZ5iq3HYrT/31LzmCLo0KtRnuoDatfaY68cIIaaqCWmMyCjripYpzNK/eMkFhCVCNSk20lAi 7tYZQsAojJKT5+22vhhehGW3Ozv X-Received: by 2002:a67:f34d:0:b0:7cc:ace:24ad with SMTP id ada2fe7eead31-7cc17c72bd9mr970380137.0.1792182566482; Fri, 16 Oct 2026 13:29:26 -0700 (PDT) Received: from localhost ([2601:19c:4a00:897d:3391:ed89:5571:41a8]) by smtp.gmail.com with ESMTPSA id a1e0cc1a2514c-990835c29eesm2271202241.4.2026.10.16.13.29.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Oct 2026 13:29:26 -0700 (PDT) From: Ray Strode To: libcamera-devel@lists.libcamera.org Cc: johannes.goede@oss.qualcomm.com, laurent.pinchart@ideasonboard.com, kieran.bingham@ideasonboard.com Subject: [PATCH 1/7] tests: Separate unit and integration suites Date: Fri, 16 Oct 2026 16:29:10 -0400 Message-ID: <20261016202916.1405813-2-halfline@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261016202916.1405813-1-halfline@gmail.com> References: <8fd58439-d19e-4539-962f-a6110be5c971@oss.qualcomm.com> <20261016202916.1405813-1-halfline@gmail.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" From: Ray Strode This commit identifies the existing tests as unit or integration tests in Meson. The test option currently enables one collection containing both self-contained component checks and tests that enumerate devices, capture frames or load real IPA modules. The collection has component suites, but no common way to select either kind of test. Each test now belongs to the unit or integration suite. Existing component suites, executable names and execution settings stay the same, preserving existing test commands and component selection. Signed-off-by: Ray Strode --- test/camera/meson.build | 2 +- test/controls/meson.build | 6 +++--- test/gstreamer/meson.build | 2 +- test/ipa/libipa/meson.build | 2 +- test/ipa/meson.build | 2 +- test/ipc/meson.build | 4 ++-- test/log/meson.build | 2 +- test/media_device/meson.build | 2 +- test/meson.build | 16 ++++++++++------ test/process/meson.build | 2 +- test/py/meson.build | 2 +- .../generated_serializer/meson.build | 2 +- test/serialization/meson.build | 2 +- test/software_isp/meson.build | 2 +- test/stream/meson.build | 2 +- test/v4l2_compat/meson.build | 2 +- test/v4l2_subdevice/meson.build | 2 +- test/v4l2_videodevice/meson.build | 2 +- 18 files changed, 30 insertions(+), 26 deletions(-) diff --git a/test/camera/meson.build b/test/camera/meson.build index 4f9f8c8c..b92e16de 100644 --- a/test/camera/meson.build +++ b/test/camera/meson.build @@ -16,5 +16,5 @@ foreach test : camera_tests dependencies : libcamera_private, link_with : test_libraries, include_directories : test_includes_internal) - test(test['name'], exe, suite : 'camera', is_parallel : false) + test(test['name'], exe, suite : ['integration', 'camera'], is_parallel : false) endforeach diff --git a/test/controls/meson.build b/test/controls/meson.build index 763f8905..cab33717 100644 --- a/test/controls/meson.build +++ b/test/controls/meson.build @@ -2,8 +2,8 @@ control_tests = [ {'name': 'control_info', 'sources': ['control_info.cpp']}, - {'name': 'control_info_map', 'sources': ['control_info_map.cpp']}, - {'name': 'control_list', 'sources': ['control_list.cpp']}, + {'name': 'control_info_map', 'sources': ['control_info_map.cpp'], 'suite': 'integration'}, + {'name': 'control_list', 'sources': ['control_list.cpp'], 'suite': 'integration'}, {'name': 'control_value', 'sources': ['control_value.cpp']}, ] @@ -12,5 +12,5 @@ foreach test : control_tests dependencies : libcamera_public, link_with : test_libraries, include_directories : test_includes_internal) - test(test['name'], exe, suite : 'controls', is_parallel : false) + test(test['name'], exe, suite : [test.get('suite', 'unit'), 'controls'], is_parallel : false) endforeach diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build index e066c582..249cf933 100644 --- a/test/gstreamer/meson.build +++ b/test/gstreamer/meson.build @@ -26,6 +26,6 @@ foreach test : gstreamer_tests link_with : test_libraries, include_directories : test_includes_internal) - test(test['name'], exe, suite : 'gstreamer', is_parallel : false, + test(test['name'], exe, suite : ['integration', 'gstreamer'], is_parallel : false, env : gst_env, should_fail : test.get('should_fail', false)) endforeach diff --git a/test/ipa/libipa/meson.build b/test/ipa/libipa/meson.build index c3e25587..c3c542b6 100644 --- a/test/ipa/libipa/meson.build +++ b/test/ipa/libipa/meson.build @@ -16,6 +16,6 @@ foreach test : libipa_test include_directories : [test_includes_internal, '../../../src/ipa/libipa/']) - test(test['name'], exe, suite : 'ipa', + test(test['name'], exe, suite : ['unit', 'ipa'], should_fail : test.get('should_fail', false)) endforeach diff --git a/test/ipa/meson.build b/test/ipa/meson.build index ceed15ba..08067092 100644 --- a/test/ipa/meson.build +++ b/test/ipa/meson.build @@ -13,5 +13,5 @@ foreach test : ipa_test link_with : [test_libraries], include_directories : [test_includes_internal]) - test(test['name'], exe, suite : 'ipa') + test(test['name'], exe, suite : ['integration', 'ipa']) endforeach diff --git a/test/ipc/meson.build b/test/ipc/meson.build index 8e447d22..394a87c1 100644 --- a/test/ipc/meson.build +++ b/test/ipc/meson.build @@ -1,7 +1,7 @@ # SPDX-License-Identifier: CC0-1.0 ipc_tests = [ - {'name': 'unixsocket_ipc', 'sources': ['unixsocket_ipc.cpp']}, + {'name': 'unixsocket_ipc', 'sources': ['unixsocket_ipc.cpp'], 'suite': 'integration'}, {'name': 'unixsocket', 'sources': ['unixsocket.cpp']}, ] @@ -11,5 +11,5 @@ foreach test : ipc_tests link_with : test_libraries, include_directories : test_includes_internal) - test(test['name'], exe, suite : 'ipc') + test(test['name'], exe, suite : [test.get('suite', 'unit'), 'ipc']) endforeach diff --git a/test/log/meson.build b/test/log/meson.build index f413c389..468e70db 100644 --- a/test/log/meson.build +++ b/test/log/meson.build @@ -11,6 +11,6 @@ foreach test : log_test link_with : test_libraries, include_directories : test_includes_internal) - test(test['name'], exe, suite : 'log', + test(test['name'], exe, suite : ['unit', 'log'], should_fail : test.get('should_fail', false)) endforeach diff --git a/test/media_device/meson.build b/test/media_device/meson.build index 84966c97..7a8a6520 100644 --- a/test/media_device/meson.build +++ b/test/media_device/meson.build @@ -20,5 +20,5 @@ foreach test : media_device_tests link_with : [test_libraries, lib_mdev_test], include_directories : test_includes_internal) - test(test['name'], exe, suite : 'media_device', is_parallel : false) + test(test['name'], exe, suite : ['integration', 'media_device'], is_parallel : false) endforeach diff --git a/test/meson.build b/test/meson.build index fc72d105..99ff5e84 100644 --- a/test/meson.build +++ b/test/meson.build @@ -52,15 +52,15 @@ public_tests = [ internal_tests = [ {'name': 'bayer-format', 'sources': ['bayer-format.cpp']}, {'name': 'byte-stream-buffer', 'sources': ['byte-stream-buffer.cpp']}, - {'name': 'camera-sensor', 'sources': ['camera-sensor.cpp']}, + {'name': 'camera-sensor', 'sources': ['camera-sensor.cpp'], 'suite': 'integration'}, {'name': 'debayer-geometry', 'sources': ['debayer-geometry.cpp']}, - {'name': 'delayed_controls', 'sources': ['delayed_controls.cpp']}, + {'name': 'delayed_controls', 'sources': ['delayed_controls.cpp'], 'suite': 'integration'}, {'name': 'event', 'sources': ['event.cpp']}, {'name': 'event-dispatcher', 'sources': ['event-dispatcher.cpp']}, {'name': 'event-thread', 'sources': ['event-thread.cpp']}, {'name': 'file', 'sources': ['file.cpp']}, {'name': 'flags', 'sources': ['flags.cpp']}, - {'name': 'hotplug-cameras', 'sources': ['hotplug-cameras.cpp']}, + {'name': 'hotplug-cameras', 'sources': ['hotplug-cameras.cpp'], 'suite': 'integration'}, {'name': 'matrix', 'sources': ['matrix.cpp']}, {'name': 'message', 'sources': ['message.cpp']}, {'name': 'object', 'sources': ['object.cpp']}, @@ -97,7 +97,9 @@ foreach test : public_tests link_with : test_libraries, include_directories : test_includes_public) - test(test['name'], exe, should_fail : test.get('should_fail', false)) + test(test['name'], exe, + suite : test.get('suite', 'unit'), + should_fail : test.get('should_fail', false)) endforeach foreach test : internal_tests @@ -112,7 +114,9 @@ foreach test : internal_tests link_with : test_libraries, include_directories : test_includes_internal) - test(test['name'], exe, should_fail : test.get('should_fail', false)) + test(test['name'], exe, + suite : test.get('suite', 'unit'), + should_fail : test.get('should_fail', false)) endforeach foreach test : internal_non_parallel_tests @@ -127,7 +131,7 @@ foreach test : internal_non_parallel_tests link_with : test_libraries, include_directories : test_includes_internal) - test(test['name'], exe, + test(test['name'], exe, suite : 'integration', is_parallel : false, should_fail : test.get('should_fail', false)) endforeach diff --git a/test/process/meson.build b/test/process/meson.build index a80dc2d9..d40b3e74 100644 --- a/test/process/meson.build +++ b/test/process/meson.build @@ -10,5 +10,5 @@ foreach test : process_tests link_with : test_libraries, include_directories : test_includes_internal) - test(test['name'], exe, suite : 'process', is_parallel : false) + test(test['name'], exe, suite : ['unit', 'process'], is_parallel : false) endforeach diff --git a/test/py/meson.build b/test/py/meson.build index d014aac4..c35209df 100644 --- a/test/py/meson.build +++ b/test/py/meson.build @@ -35,5 +35,5 @@ test('pyunittests', py3, args : files('unittests.py'), env : py_env, - suite : 'pybindings', + suite : ['integration', 'pybindings'], is_parallel : false) diff --git a/test/serialization/generated_serializer/meson.build b/test/serialization/generated_serializer/meson.build index 9fb9cd1d..6595164c 100644 --- a/test/serialization/generated_serializer/meson.build +++ b/test/serialization/generated_serializer/meson.build @@ -16,4 +16,4 @@ exe = executable('generated_serializer_test', ]) test('generated_serializer_test', exe, - suite : 'generated_serializer', is_parallel : false) + suite : ['unit', 'generated_serializer'], is_parallel : false) diff --git a/test/serialization/meson.build b/test/serialization/meson.build index a6e8d793..77cfa8c3 100644 --- a/test/serialization/meson.build +++ b/test/serialization/meson.build @@ -12,5 +12,5 @@ foreach test : serialization_tests dependencies : libcamera_private, link_with : test_libraries, include_directories : test_includes_internal) - test(test['name'], exe, suite : 'serialization', is_parallel : false) + test(test['name'], exe, suite : ['integration', 'serialization'], is_parallel : false) endforeach diff --git a/test/software_isp/meson.build b/test/software_isp/meson.build index ebabde59..539278ad 100644 --- a/test/software_isp/meson.build +++ b/test/software_isp/meson.build @@ -16,5 +16,5 @@ foreach test : software_isp_tests include_directories : [test_includes_internal, '../../src/libcamera/software_isp/']) - test(test['name'], exe, suite : 'software_isp') + test(test['name'], exe, suite : ['unit', 'software_isp']) endforeach diff --git a/test/stream/meson.build b/test/stream/meson.build index dd77f2f7..dc9468c7 100644 --- a/test/stream/meson.build +++ b/test/stream/meson.build @@ -10,5 +10,5 @@ foreach test : stream_tests dependencies : libcamera_public, link_with : test_libraries, include_directories : test_includes_internal) - test(test['name'], exe, suite : 'stream') + test(test['name'], exe, suite : ['unit', 'stream']) endforeach diff --git a/test/v4l2_compat/meson.build b/test/v4l2_compat/meson.build index 2691eacf..01045a83 100644 --- a/test/v4l2_compat/meson.build +++ b/test/v4l2_compat/meson.build @@ -25,5 +25,5 @@ v4l2_compat_args += [v4l2_compat] test('v4l2_compat_test', v4l2_compat_test, args : v4l2_compat_args, - suite : 'v4l2_compat', + suite : ['integration', 'v4l2_compat'], timeout : 60) diff --git a/test/v4l2_subdevice/meson.build b/test/v4l2_subdevice/meson.build index 277f29bb..68255717 100644 --- a/test/v4l2_subdevice/meson.build +++ b/test/v4l2_subdevice/meson.build @@ -10,5 +10,5 @@ foreach test : v4l2_subdevice_tests dependencies : libcamera_private, link_with : test_libraries, include_directories : test_includes_internal) - test(test['name'], exe, suite : 'v4l2_subdevice', is_parallel : false) + test(test['name'], exe, suite : ['integration', 'v4l2_subdevice'], is_parallel : false) endforeach diff --git a/test/v4l2_videodevice/meson.build b/test/v4l2_videodevice/meson.build index 87ea4f96..486e831c 100644 --- a/test/v4l2_videodevice/meson.build +++ b/test/v4l2_videodevice/meson.build @@ -20,5 +20,5 @@ foreach test : v4l2_videodevice_tests dependencies : libcamera_private, link_with : test_libraries, include_directories : test_includes_internal) - test(test['name'], exe, suite : 'v4l2_videodevice', is_parallel : false) + test(test['name'], exe, suite : ['integration', 'v4l2_videodevice'], is_parallel : false) endforeach From patchwork Fri Oct 16 20:29:11 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ray Strode X-Patchwork-Id: 28486 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id D6B2EC324C for ; Fri, 16 Oct 2026 20:29:31 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 6C4B668E7D; Fri, 16 Oct 2026 22:29:31 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="ZTyasbEG"; dkim-atps=neutral Received: from mail-vs1-xe2d.google.com (mail-vs1-xe2d.google.com [IPv6:2607:f8b0:4864:20::e2d]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B2D4E68E5B for ; Fri, 16 Oct 2026 22:29:28 +0200 (CEST) Received: by mail-vs1-xe2d.google.com with SMTP id ada2fe7eead31-7cbbb7b90efso62869137.2 for ; Fri, 16 Oct 2026 13:29:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792182567; x=1792787367; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=Y42NSwy8HJ/ZqSzPA5FUW17P6X6kOCCgthInMKKbfE0=; b=ZTyasbEG5NZJt8IKqSwPKVnCR1EgI+tsBYUOq7Y0QUzlT815O8Al1onQEzPrANqRtp 3eWCgsiHACfqbCJVrDkKHuusmT4nRXHokQjr+lG+fMtmL4qS54o3Q0eV8JDJICXUiOKn AfWmkyxKgWEBhFGotrAz/fWwAEDMxkQVX5N0eFj2/KiwJt7mLNurpaB+aixrHe3wfIJ6 cHxf95ocSIPKW+PXNdiBJnlMZwDd7BWQLTm7UAwaCJGfZziCosBiKUhjNagS23jTxxNo 6W0eGFlrk4puD0GcWoWX8FGwa6oMkn/mi8OYewktlzmr4dzJAEyg4ZBgfNc5NKudlXkJ s8Bw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792182567; x=1792787367; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=Y42NSwy8HJ/ZqSzPA5FUW17P6X6kOCCgthInMKKbfE0=; b=oRCDljiTQWbFVS+uhSvyd2vbC6l5CMhJEya6a7xrzaVF5rxE1Hu4UnI09k91O8jY6P o22s3FMjxVSLcthBL+32k7FPE55d6+IdbW3ONbRxWnF3VqObvV73pOynm2Czydgbz35E CRNySkOv8nPgPsrogfgae7VFbOri5D/h4AG3M/e/QZ38LUQsUSpoeLsFuiKx4FZNjdhx aQ1S7L/yJgCr/lZDdvo1jI4CJXgL3cp328YZUtYtsX//OTQddYJsK5vcpQrzmDEnTxBJ kNy5ZZzKYgB7tkDqzlEnRuQM6TUqWeDVwu/CnI8jeBrPPiEsfbWWFnpJfmTMSxEiipnx VaDQ== X-Gm-Message-State: AFq9FYKhbYLVxV41CtiBYS4l0SvRDvDXBY1WqdO4gh4W0rbKq1kmiVo6 k3sroASe+EnbRsYghTwrSlbA8ySfRfEVciKXTXuX32twQwDjQoTZ5t298FszqJzFNZc= X-Gm-Gg: AYBFou0qer7Ih4EBA0IQIWwh5ectU30PtWdR/Nubp4L65bfQKvQ4iEM8dSF9HdY3kfK 9sMN2tSdVMyI3zov1JZkjgTZudpuRYraGlddh/M1cLvUCGWf4luNi+WBALocs1zczYvBzJJ7TFI 3H/ZsvuugXcGuQfqb8WQu/fiutXx7pVLLhoBed/fcBjTfXIVZt4+kM7YyqhJKg3K/C7mQQsMzN7 lWi1KxZvmUM7QMqZBUnwbcjj3NSrlkhCvAtw7k+gqTeLeDqBnSUIZCYxZsFvgFLfWbfJN4Shplu PXx+ZLjErmh6K80p4HE8AIzJ6IuwVQlgtUyVhGPua+5SdZv0wq1bjarsE/Grh+GhbLIwHnjpnDs EMA4FDl+2vIcAYfj0mVBbJpXZs122Y9vS3HHsFiA9+f4cjFEeMiD5jk9Cg/pGv5kp6ppKCpXeK3 jsIlcYj3vD3K/fbj0LVbhNGdkOLJUfBVlsAtUrSs+GKpYgcw6aOx0Q+jyMB5m4VaW32QT6BqbYd Mv2VLNuceHWeYLD579saSmVQMcCrQ== X-Received: by 2002:a05:6102:955:b0:7cc:2946:7f69 with SMTP id ada2fe7eead31-7cc368fe22bmr204607137.8.1792182567434; Fri, 16 Oct 2026 13:29:27 -0700 (PDT) Received: from localhost ([2601:19c:4a00:897d:3391:ed89:5571:41a8]) by smtp.gmail.com with ESMTPSA id a1e0cc1a2514c-990835c29eesm2271202241.4.2026.10.16.13.29.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Oct 2026 13:29:26 -0700 (PDT) From: Ray Strode To: libcamera-devel@lists.libcamera.org Cc: johannes.goede@oss.qualcomm.com, laurent.pinchart@ideasonboard.com, kieran.bingham@ideasonboard.com Subject: [PATCH 2/7] tests: Select unit and integration suites independently Date: Fri, 16 Oct 2026 16:29:11 -0400 Message-ID: <20261016202916.1405813-3-halfline@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261016202916.1405813-1-halfline@gmail.com> References: <8fd58439-d19e-4539-962f-a6110be5c971@oss.qualcomm.com> <20261016202916.1405813-1-halfline@gmail.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" From: Ray Strode This commit adds matching build options for the unit and integration suites. Meson can select either suite when running tests, but the test option still compiles both groups. Device and pipeline fixtures therefore remain part of a build intended only for self-contained component tests. The test-unit and test-integration options select their respective groups independently. The existing test option enables both, preserving the tests included by existing build commands. All three options are disabled by default. Only integration tests automatically enable the vimc pipeline and its IPA fixtures. Both groups share the existing test framework, and integration subdirectories can add further backend checks to that group. Signed-off-by: Ray Strode --- meson.build | 12 +++++--- meson_options.txt | 12 +++++++- src/ipa/meson.build | 6 ++-- test/camera/meson.build | 4 +++ test/controls/meson.build | 4 +++ test/gstreamer/meson.build | 4 +++ test/integration/meson.build | 3 ++ test/ipa/libipa/meson.build | 4 +++ test/ipa/meson.build | 4 +++ test/ipc/meson.build | 4 +++ test/log/meson.build | 4 +++ test/media_device/meson.build | 4 +++ test/meson.build | 29 ++++++++++++++++--- test/process/meson.build | 4 +++ test/py/meson.build | 4 +++ .../generated_serializer/meson.build | 4 +++ test/serialization/meson.build | 4 +++ test/software_isp/meson.build | 2 +- test/stream/meson.build | 4 +++ test/v4l2_compat/meson.build | 4 +++ test/v4l2_subdevice/meson.build | 4 +++ test/v4l2_videodevice/meson.build | 4 +++ 22 files changed, 115 insertions(+), 13 deletions(-) create mode 100644 test/integration/meson.build diff --git a/meson.build b/meson.build index 37fe9ce4..99174b6e 100644 --- a/meson.build +++ b/meson.build @@ -241,9 +241,12 @@ else pipelines = wanted_pipelines endif -# Tests require the vimc pipeline handler, include it automatically when tests -# are enabled. -if get_option('test') +test_unit_enabled = get_option('test') or get_option('test-unit') +test_integration_enabled = get_option('test') or get_option('test-integration') + +# Integration tests require the vimc pipeline handler, include it automatically +# when they are enabled. +if test_integration_enabled foreach pipeline, archs : pipelines_support if 'test' in archs and pipeline not in pipelines message('Enabling ' + pipeline + ' pipeline handler for tests') @@ -304,7 +307,8 @@ summary({ 'GStreamer support': gst_enabled, 'Python bindings': pycamera_enabled, 'V4L2 emulation support': v4l2_enabled, - 'Unit tests': test_enabled, + 'Unit tests': test_unit_enabled, + 'Integration tests': test_integration_enabled, }, section : 'Configuration', bool_yn : true) diff --git a/meson_options.txt b/meson_options.txt index e829ebe6..fffdb6de 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -114,7 +114,17 @@ option('softisp-gpu', option('test', type : 'boolean', value : false, - description : 'Compile and include the tests') + description : 'Compile and include all tests') + +option('test-unit', + type : 'boolean', + value : false, + description : 'Compile and include unit tests') + +option('test-integration', + type : 'boolean', + value : false, + description : 'Compile and include integration tests') option('tracing', type : 'feature', diff --git a/src/ipa/meson.build b/src/ipa/meson.build index ab4a1897..200fad8d 100644 --- a/src/ipa/meson.build +++ b/src/ipa/meson.build @@ -36,9 +36,9 @@ supported_ipas = { ipa_modules = get_option('ipas') -# Tests require the vimc IPA, similar to vimc pipline-handler for their -# execution. Include it automatically when tests are enabled. -if get_option('test') and 'vimc' not in ipa_modules +# Integration tests require the vimc IPA, similar to the vimc pipeline +# handler. Include it automatically when integration tests are enabled. +if test_integration_enabled and 'vimc' not in ipa_modules message('Enabling vimc IPA to support tests') ipa_modules += ['vimc'] endif diff --git a/test/camera/meson.build b/test/camera/meson.build index b92e16de..419915ef 100644 --- a/test/camera/meson.build +++ b/test/camera/meson.build @@ -1,5 +1,9 @@ # SPDX-License-Identifier: CC0-1.0 +if not test_integration_enabled + subdir_done() +endif + # Tests are listed in order of complexity. # They are not alphabetically sorted. camera_tests = [ diff --git a/test/controls/meson.build b/test/controls/meson.build index cab33717..745212df 100644 --- a/test/controls/meson.build +++ b/test/controls/meson.build @@ -8,6 +8,10 @@ control_tests = [ ] foreach test : control_tests + if test.get('suite', 'unit') not in test_suites + continue + endif + exe = executable(test['name'], test['sources'], dependencies : libcamera_public, link_with : test_libraries, diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build index 249cf933..4fd7a530 100644 --- a/test/gstreamer/meson.build +++ b/test/gstreamer/meson.build @@ -1,5 +1,9 @@ # SPDX-License-Identifier: CC0-1.0 +if not test_integration_enabled + subdir_done() +endif + if not gst_enabled subdir_done() endif diff --git a/test/integration/meson.build b/test/integration/meson.build new file mode 100644 index 00000000..51e97101 --- /dev/null +++ b/test/integration/meson.build @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: CC0-1.0 + +# Integration test subdirectories register tests in the 'integration' suite. diff --git a/test/ipa/libipa/meson.build b/test/ipa/libipa/meson.build index c3c542b6..d6f13323 100644 --- a/test/ipa/libipa/meson.build +++ b/test/ipa/libipa/meson.build @@ -1,5 +1,9 @@ # SPDX-License-Identifier: CC0-1.0 +if not test_unit_enabled + subdir_done() +endif + libipa_test = [ {'name': 'fixedpoint', 'sources': ['fixedpoint.cpp']}, {'name': 'histogram', 'sources': ['histogram.cpp']}, diff --git a/test/ipa/meson.build b/test/ipa/meson.build index 08067092..9faace7e 100644 --- a/test/ipa/meson.build +++ b/test/ipa/meson.build @@ -2,6 +2,10 @@ subdir('libipa') +if not test_integration_enabled + subdir_done() +endif + ipa_test = [ {'name': 'ipa_module_test', 'sources': ['ipa_module_test.cpp']}, {'name': 'ipa_interface_test', 'sources': ['ipa_interface_test.cpp']}, diff --git a/test/ipc/meson.build b/test/ipc/meson.build index 394a87c1..e7edd236 100644 --- a/test/ipc/meson.build +++ b/test/ipc/meson.build @@ -6,6 +6,10 @@ ipc_tests = [ ] foreach test : ipc_tests + if test.get('suite', 'unit') not in test_suites + continue + endif + exe = executable(test['name'], test['sources'], dependencies : libcamera_private, link_with : test_libraries, diff --git a/test/log/meson.build b/test/log/meson.build index 468e70db..44b25f25 100644 --- a/test/log/meson.build +++ b/test/log/meson.build @@ -1,5 +1,9 @@ # SPDX-License-Identifier: CC0-1.0 +if not test_unit_enabled + subdir_done() +endif + log_test = [ {'name': 'log_api', 'sources': ['log_api.cpp']}, {'name': 'log_process', 'sources': ['log_process.cpp']}, diff --git a/test/media_device/meson.build b/test/media_device/meson.build index 7a8a6520..a5873abc 100644 --- a/test/media_device/meson.build +++ b/test/media_device/meson.build @@ -1,5 +1,9 @@ # SPDX-License-Identifier: CC0-1.0 +if not test_integration_enabled + subdir_done() +endif + lib_mdev_test_sources = files([ 'media_device_test.cpp', ]) diff --git a/test/meson.build b/test/meson.build index 99ff5e84..79f4b1b7 100644 --- a/test/meson.build +++ b/test/meson.build @@ -1,11 +1,16 @@ # SPDX-License-Identifier: CC0-1.0 -if not get_option('test') - test_enabled = false - subdir_done() +test_suites = [] +if test_unit_enabled + test_suites += 'unit' +endif +if test_integration_enabled + test_suites += 'integration' endif -test_enabled = true +if test_suites.length() == 0 + subdir_done() +endif # When ASan is enabled, find the path to the ASan runtime needed by multiple # tests. This currently works with gcc only, as clang uses different file names @@ -25,6 +30,10 @@ endif subdir('libtest') +if test_integration_enabled + subdir('integration') +endif + subdir('camera') subdir('controls') subdir('gstreamer') @@ -86,6 +95,10 @@ internal_non_parallel_tests = [ ] foreach test : public_tests + if not test_unit_enabled + continue + endif + deps = [libcamera_public] if 'dependencies' in test deps += test['dependencies'] @@ -103,6 +116,10 @@ foreach test : public_tests endforeach foreach test : internal_tests + if test.get('suite', 'unit') not in test_suites + continue + endif + deps = [libcamera_private] if 'dependencies' in test deps += test['dependencies'] @@ -120,6 +137,10 @@ foreach test : internal_tests endforeach foreach test : internal_non_parallel_tests + if not test_integration_enabled + continue + endif + deps = [libcamera_private] if 'dependencies' in test deps += test['dependencies'] diff --git a/test/process/meson.build b/test/process/meson.build index d40b3e74..4e2cc982 100644 --- a/test/process/meson.build +++ b/test/process/meson.build @@ -1,5 +1,9 @@ # SPDX-License-Identifier: CC0-1.0 +if not test_unit_enabled + subdir_done() +endif + process_tests = [ {'name': 'process_test', 'sources': ['process_test.cpp']}, ] diff --git a/test/py/meson.build b/test/py/meson.build index c35209df..45194abd 100644 --- a/test/py/meson.build +++ b/test/py/meson.build @@ -1,5 +1,9 @@ # SPDX-License-Identifier: CC0-1.0 +if not test_integration_enabled + subdir_done() +endif + if not pycamera_enabled subdir_done() endif diff --git a/test/serialization/generated_serializer/meson.build b/test/serialization/generated_serializer/meson.build index 6595164c..b6271370 100644 --- a/test/serialization/generated_serializer/meson.build +++ b/test/serialization/generated_serializer/meson.build @@ -1,5 +1,9 @@ # SPDX-License-Identifier: CC0-1.0 +if not test_unit_enabled + subdir_done() +endif + subdir('include/libcamera/ipa') exe = executable('generated_serializer_test', diff --git a/test/serialization/meson.build b/test/serialization/meson.build index 77cfa8c3..d786b8da 100644 --- a/test/serialization/meson.build +++ b/test/serialization/meson.build @@ -2,6 +2,10 @@ subdir('generated_serializer') +if not test_integration_enabled + subdir_done() +endif + serialization_tests = [ {'name': 'control_serialization', 'sources': ['control_serialization.cpp']}, {'name': 'ipa_data_serializer_test', 'sources': ['ipa_data_serializer_test.cpp']}, diff --git a/test/software_isp/meson.build b/test/software_isp/meson.build index 539278ad..c49ab8ac 100644 --- a/test/software_isp/meson.build +++ b/test/software_isp/meson.build @@ -1,6 +1,6 @@ # SPDX-License-Identifier: CC0-1.0 -if not softisp_enabled +if not test_unit_enabled or not softisp_enabled subdir_done() endif diff --git a/test/stream/meson.build b/test/stream/meson.build index dc9468c7..c7cf7c5a 100644 --- a/test/stream/meson.build +++ b/test/stream/meson.build @@ -1,5 +1,9 @@ # SPDX-License-Identifier: CC0-1.0 +if not test_unit_enabled + subdir_done() +endif + stream_tests = [ {'name': 'stream_colorspace', 'sources': ['stream_colorspace.cpp']}, {'name': 'stream_formats', 'sources': ['stream_formats.cpp']}, diff --git a/test/v4l2_compat/meson.build b/test/v4l2_compat/meson.build index 01045a83..38be3971 100644 --- a/test/v4l2_compat/meson.build +++ b/test/v4l2_compat/meson.build @@ -1,5 +1,9 @@ # SPDX-License-Identifier: CC0-1.0 +if not test_integration_enabled + subdir_done() +endif + if not is_variable('v4l2_compat') subdir_done() endif diff --git a/test/v4l2_subdevice/meson.build b/test/v4l2_subdevice/meson.build index 68255717..59f4e5ec 100644 --- a/test/v4l2_subdevice/meson.build +++ b/test/v4l2_subdevice/meson.build @@ -1,5 +1,9 @@ # SPDX-License-Identifier: CC0-1.0 +if not test_integration_enabled + subdir_done() +endif + v4l2_subdevice_tests = [ {'name': 'list_formats', 'sources': ['list_formats.cpp']}, {'name': 'test_formats', 'sources': ['test_formats.cpp']}, diff --git a/test/v4l2_videodevice/meson.build b/test/v4l2_videodevice/meson.build index 486e831c..3be95651 100644 --- a/test/v4l2_videodevice/meson.build +++ b/test/v4l2_videodevice/meson.build @@ -1,5 +1,9 @@ # SPDX-License-Identifier: CC0-1.0 +if not test_integration_enabled + subdir_done() +endif + # Tests are listed in order of complexity. # They are not alphabetically sorted. v4l2_videodevice_tests = [ From patchwork Fri Oct 16 20:29:12 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ray Strode X-Patchwork-Id: 28487 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id B072DC324C for ; Fri, 16 Oct 2026 20:29:33 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 2BDD368E84; Fri, 16 Oct 2026 22:29:33 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="VqwY9iWR"; dkim-atps=neutral Received: from mail-vk1-xa29.google.com (mail-vk1-xa29.google.com [IPv6:2607:f8b0:4864:20::a29]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B8EF468E61 for ; Fri, 16 Oct 2026 22:29:29 +0200 (CEST) Received: by mail-vk1-xa29.google.com with SMTP id 71dfb90a1353d-5e948e56b9dso117808e0c.1 for ; Fri, 16 Oct 2026 13:29:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792182568; x=1792787368; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=7JgRT+BX6Aya5EQRexwkz9yF0W7EjIR4PTkrhlEKUoQ=; b=VqwY9iWRWeVpco+D7dnVm5eEokOHh0xaivG63V6E0KhynOk/rrYY0ckgiUzpcINQNc WTTfLc6eH/65nyPLWBNQkLW+Qy9y1irTyDo/B56VjksI90W7Kf5TUe+4PUhnGSBn0rjQ lS36Nlb+A5OtVSVA3Hxjt/RWZX52DOskSuqJO0njsdkz8XfPsjvxnmlJWfeWUrrBYbpm +O94+g1xUDhm1Qx4yj6CxTeMTqxo732tYUUiwWN7GQylGEQ+gmtV2KX0PqAUtmEh4aSX hKsllh7I7jBb3dilryQXEBV8NtPQgw2IS9DoQngJ6yP/2dWr/UYCna/S2Objn5ltiA86 1d6w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792182568; x=1792787368; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=7JgRT+BX6Aya5EQRexwkz9yF0W7EjIR4PTkrhlEKUoQ=; b=fNbu7mgS0uwKCBkNN1Kh+NjUEiF6+lBoYc/4CduUtE2rkyI+axQslhre8OBNkqcgsk l1lzItwZWv+AiVCWNVm+axwiBrQY9TIwqc5vT8UwWry6tSzFnk1pKj5IjrZRTtd5xO/Y t6ZDKtVgMTJ2V07adLBQ83LZxQPgMJ9xGW57+8wON0VoUid/NpthaLqJt8dvoUpYYT6R dIusQuYQ4bbverjyH+H+dRv/bh+8PCccB0qn/1IB+Fi4F4+EQ6wvGG3BhBb10c1LDIVX +jDpFXwYr3egW42AlfsLoZ00BeI/yXAA0Nj9pLFd0jpspLYxvWLznNq+rkEZp07BadLZ KM6g== X-Gm-Message-State: AFq9FYKo5ItNLIGYYKX6Uc6H9cGDrEG8PE39bTvfe8Kv6UD1mqziMns5 SVG0qbp4bYlBTMezdn4EKvvoyQJU37DJxWMUJg3CZdT6H0eiGHEVUdgLX/p/oKAIRMo= X-Gm-Gg: AYBFou01Hut2E4mJYLmjZjP7K07NRiUmyR3LwfJ8rdsJ6M8hdo9GvKBfskLkkdTG6iG n8CnAztooWcshGHuoPiFi++qgbSFveItexF2z+iX/PEZtkQAYjYWfxjea4ALDDsKT4QHOewF9fI BZa3CM8HjEX7NA4PvXJKB3KX08J/Im7peAXQ1jveBI9bqdX6n/w2ct2LZQLvhokmmTkY7hUJB73 heDcMebNk9K3rsN2ajjRFBozxC6zhwWQj+cg11mw9ER7GWf0W/F7H/norjiaNHi3/2UP/l+yFVK R/ZDOvUhdCkSaKL9poMcLyC+G5fXmwogY0aqeycko5dAhNCwmUtlX3xWsn+NLcX2Cj8IcOaa+a9 kfkXf1O33KpJNIBS1zR28dQl6UFcp/MXbrTPq8ANr+AE4RpLxPcCYoDzgSXuR+Uw2IVifgWbxLG N5tKPvBTa1fAiUUKkaQYnES1V44cHtiUymcWHz/yd1Hk159jayYb5MDN9QcfD2oWN1Iapqd0H5X 9c0/yHiQOkFv/DGM3geB1F+t2B/ X-Received: by 2002:a05:6102:2906:b0:7c3:43cd:6fa6 with SMTP id ada2fe7eead31-7cc3699f2f9mr219455137.18.1792182568437; Fri, 16 Oct 2026 13:29:28 -0700 (PDT) Received: from localhost ([2601:19c:4a00:897d:3391:ed89:5571:41a8]) by smtp.gmail.com with ESMTPSA id a1e0cc1a2514c-990835c29eesm2271202241.4.2026.10.16.13.29.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Oct 2026 13:29:27 -0700 (PDT) From: Ray Strode To: libcamera-devel@lists.libcamera.org Cc: johannes.goede@oss.qualcomm.com, laurent.pinchart@ideasonboard.com, kieran.bingham@ideasonboard.com Subject: [PATCH 3/7] docs: Describe unit and integration test selection Date: Fri, 16 Oct 2026 16:29:12 -0400 Message-ID: <20261016202916.1405813-4-halfline@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261016202916.1405813-1-halfline@gmail.com> References: <8fd58439-d19e-4539-962f-a6110be5c971@oss.qualcomm.com> <20261016202916.1405813-1-halfline@gmail.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" From: Ray Strode This commit documents the distinction between the existing test suites and how to build and run each group. The guide explains the fixtures used by each suite, the retained component suites, and the test-unit and test-integration options alongside the existing test option. It also explains the vimc pipeline dependency and how to change an existing build to select only one group. Signed-off-by: Ray Strode --- Documentation/index.rst | 1 + Documentation/meson.build | 1 + Documentation/testing.rst | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 Documentation/testing.rst diff --git a/Documentation/index.rst b/Documentation/index.rst index e40cd0c9..bc5f849e 100644 --- a/Documentation/index.rst +++ b/Documentation/index.rst @@ -25,6 +25,7 @@ Tracing guide Camera Sensor Model SoftwareISP Benchmarking + Testing Design document: AE Internal API Contribute diff --git a/Documentation/meson.build b/Documentation/meson.build index a156bd0b..69819776 100644 --- a/Documentation/meson.build +++ b/Documentation/meson.build @@ -175,6 +175,7 @@ if sphinx.found() 'sensor_driver_requirements.rst', 'sensor-support.rst', 'software-isp-benchmarking.rst', + 'testing.rst', '../README.rst', ] diff --git a/Documentation/testing.rst b/Documentation/testing.rst new file mode 100644 index 00000000..49c1f085 --- /dev/null +++ b/Documentation/testing.rst @@ -0,0 +1,35 @@ +.. SPDX-License-Identifier: CC-BY-SA-4.0 + +Testing libcamera +================= + +Test suites +----------- + +Unit tests check individual components with self-contained fixtures. They +include geometry, controls, serialization and the operating-system wrappers +for files, timers, threads, processes and sockets. + +Integration tests combine components with device, pipeline or external +backend fixtures. Existing checks cover camera capture, V4L2 devices, +media graphs, IPA loading and communication, and camera-backed controls +and serialization. Tests keep their component suites, such as ``camera`` +and ``ipa``, alongside the ``unit`` or ``integration`` suite. + +The ``test-unit`` and ``test-integration`` Meson options enable their +respective groups independently. The existing ``test`` option enables +both groups. All three options are disabled by default. + +.. code-block:: shell + + meson setup build-unit -Dtest-unit=true + ninja -C build-unit + meson test -C build-unit --suite unit --print-errorlogs + +Use ``-Dtest-integration=true`` to build integration tests, or ``-Dtest=true`` +to build both groups. To switch an existing build from both groups to one, +set ``-Dtest=false`` as well as enabling the desired group. + +Many integration tests need the kernel's vimc or vivid test devices. +Integration builds automatically include the vimc pipeline handler and its +IPA. Unit-only selection does not add that pipeline. From patchwork Fri Oct 16 20:29:13 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ray Strode X-Patchwork-Id: 28488 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id F1027C3305 for ; Fri, 16 Oct 2026 20:29:34 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 9BA8968E7C; Fri, 16 Oct 2026 22:29:34 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="XuvoLmsu"; dkim-atps=neutral Received: from mail-vs1-xe31.google.com (mail-vs1-xe31.google.com [IPv6:2607:f8b0:4864:20::e31]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 91A5F68E61 for ; Fri, 16 Oct 2026 22:29:30 +0200 (CEST) Received: by mail-vs1-xe31.google.com with SMTP id ada2fe7eead31-745497ac8fcso148110137.0 for ; Fri, 16 Oct 2026 13:29:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792182569; x=1792787369; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=jn4cUcqpLdZAExuB9UohVdcZKD+3flT4y6tTvORenP4=; b=XuvoLmsuVhbgl3WBqZp71ox6CmLWGwao0siXuQxAADl4rdaqGPmscviDaxitZO0Oqd y1E67Ne6BOxGbZIffpJCViBKQ35I0J3ivCkIwysO3Otp0zRS8C/b0HDkD2x5sB62hMBa 9A6gScZIfKR3Zzs7DyTqQssLmzReHGFT27dbaVbCCSq51ipGQMOhhCUMsqMkNcjs1vsC f2iWLSHI78j5NVlK0IkHEwlZKfz8ECk0neXjLPzP+H7s4y4XG4MUMNEOX16IujYJT/Wo RDPu4aSfhMyI49A8d/lQ6AZ0/+MBJMLcSZ2bLTvEhFxkYEIqL7ZSykcqYg49+M5El3K5 dAWw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792182569; x=1792787369; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=jn4cUcqpLdZAExuB9UohVdcZKD+3flT4y6tTvORenP4=; b=pCgr50EeR3DW9/Hnq9H8TdTzbJG/1wlbBVNbBjgmrWo8G4xdHlPMGlEx6KAfyNWgUy bzwpiOtEfbQlxWSL1B3fp0mlcosHbi550cvxRD54g4yjVQRjRNDHvLo/ua2Hsd11okuG EcZnQeP4rOpqTqJ3azrP9qIbRKMbJKV8RJKTMlQM713HNWxUNV66UmtRb00txUg8Wl6l 6h2b52OXcxUMDPO++0nHvMymFLNiYxVoBRWQqfE32pZj34vnQCXCc3fvaspKdCTCfchf wnQrhVj3oHbsLC2n7yHt4mvPoGdu6k40XGRXJHOaTKw6uaiY5fd59tKm3057NK1rnxMN v05A== X-Gm-Message-State: AFq9FYInKSwymhGDv14nc6HVHlJSY9VEP2x6Wd/2+/RxkkyNhYCwCoo9 iHexe7evCzIGFEK/xjBu2zoNCi7JctyApXpedFfU9xvFmQtyL7Rve9nMGuMRlYmU+sQ= X-Gm-Gg: AYBFou3uoX5b6s32spl5iOx7e9A/ZTF1FwBcgHn79v7LJNkJknCOYTbzX9NX6iIRYBS /M7fieWNktLmrzA6EXBxsvocosY8GkyHcKASsjadT/tFSgoOWvvBw7eaAM7PGzQztNf69+LybEc 6ZKK/Ew9VN6fwzsutpwBnw7+vl8T7qC5robIy7HfrfHfZU7R22Lzm8JDyCMddViLB2LIp+HlsRU dmG5OSa2Cl5M0xjBl7/JTpOWIc84a9OuFSEMfiXxjdJ3HO5r9wwDtFcsyG7O7Zw5e8uPvLolu8J aOf1nFWpiLw2YEQ+WFj6bjF74Cbwj+ra14VNtyo1US5KDyL7ACYwcM9/qBMSAIvnYKt4Sibz0wD kxsI7SUcofeizjSdHEUqvfmVvTdOI8XzfOYJOQajc9GJPqZwFjNncurlsi5AfcAuYJNIj+kJ6BK cEWeS03tBqSYg+XWgC7I6p/8ZR6Bs8B6btzWAskMoBHeu6nlnT4w/38mFqA2gqQ6lKYCbY29xxI lAfXNW5OwTUbL9S2LrahjV8QPfw X-Received: by 2002:a05:6102:3753:b0:7a7:c789:881 with SMTP id ada2fe7eead31-7cc368f6d1amr213287137.6.1792182569415; Fri, 16 Oct 2026 13:29:29 -0700 (PDT) Received: from localhost ([2601:19c:4a00:897d:3391:ed89:5571:41a8]) by smtp.gmail.com with ESMTPSA id a1e0cc1a2514c-990835c29eesm2271202241.4.2026.10.16.13.29.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Oct 2026 13:29:28 -0700 (PDT) From: Ray Strode To: libcamera-devel@lists.libcamera.org Cc: johannes.goede@oss.qualcomm.com, laurent.pinchart@ideasonboard.com, kieran.bingham@ideasonboard.com Subject: [PATCH 4/7] software-isp: Allow drawing into existing EGL textures Date: Fri, 16 Oct 2026 16:29:13 -0400 Message-ID: <20261016202916.1405813-5-halfline@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261016202916.1405813-1-halfline@gmail.com> References: <8fd58439-d19e-4539-962f-a6110be5c971@oss.qualcomm.com> <20261016202916.1405813-1-halfline@gmail.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" From: Ray Strode This commit lets EGL debayering use textures supplied by the caller for its input and output. The drawing path currently obtains its textures from frame buffers, and the output requires DMA-BUF import. Integration tests need to exercise the same shaders, uniforms, projection and viewport with CPU-uploaded textures on software renderers that do not support that import path. The frame-processing path uses the same drawing operation after obtaining its textures. Buffer import, synchronization and statistics stay with frame processing. Drawing reports an incomplete output framebuffer as an error. Signed-off-by: Ray Strode --- src/libcamera/software_isp/debayer_egl.cpp | 25 ++++++++++++++++++++-- src/libcamera/software_isp/debayer_egl.h | 3 +++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp index cd9ad888..afe78384 100644 --- a/src/libcamera/software_isp/debayer_egl.cpp +++ b/src/libcamera/software_isp/debayer_egl.cpp @@ -573,8 +573,29 @@ int DebayerEGL::debayerGPU(FrameBuffer *input, FrameBuffer *output, const Debaye if (!eglImageOut) return -ENOMEM; - egl_.attachTextureToFBO(*eglImageOut); - setShaderVariableValues(*eglImageIn, params); + return render(*eglImageIn, *eglImageOut, params); +} + +/** + * \brief Render the configured debayer operation into existing textures + * \param[in] input Texture containing the configured Bayer input + * \param[in,out] output Texture receiving the configured output image + * \param[in] params Image processing parameters + * + * The caller owns the texture storage and must start the debayer before + * rendering. Textures must belong to the debayer's current EGL context. + * + * \return 0 on success or a negative error code otherwise + */ +int DebayerEGL::render(eGLImage &input, eGLImage &output, const DebayerParams ¶ms) +{ + egl_.assertThread(); + + int ret = egl_.attachTextureToFBO(output); + if (ret) + return ret; + + setShaderVariableValues(input, params); glViewport(0, 0, width_, height_); glClear(GL_COLOR_BUFFER_BIT); diff --git a/src/libcamera/software_isp/debayer_egl.h b/src/libcamera/software_isp/debayer_egl.h index 5a4cd2d8..d18dc3fb 100644 --- a/src/libcamera/software_isp/debayer_egl.h +++ b/src/libcamera/software_isp/debayer_egl.h @@ -62,6 +62,9 @@ public: SizeRange sizes(PixelFormat inputFormat, const Size &inputSize) override; Rectangle inputCrop() const override { return inputCrop_; } +protected: + int render(eGLImage &input, eGLImage &output, const DebayerParams ¶ms); + private: static int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config); int initBayerShaders(PixelFormat inputFormat, PixelFormat outputFormat); From patchwork Fri Oct 16 20:29:14 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ray Strode X-Patchwork-Id: 28489 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 1F308C324C for ; Fri, 16 Oct 2026 20:29:36 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C5E7068E7A; Fri, 16 Oct 2026 22:29:35 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="M/Qc6HsT"; dkim-atps=neutral Received: from mail-ua1-x933.google.com (mail-ua1-x933.google.com [IPv6:2607:f8b0:4864:20::933]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B0C4F68E51 for ; Fri, 16 Oct 2026 22:29:31 +0200 (CEST) Received: by mail-ua1-x933.google.com with SMTP id a1e0cc1a2514c-990703f817fso180540241.1 for ; Fri, 16 Oct 2026 13:29:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792182570; x=1792787370; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=1LGZ+rGrPlSG7hr8w6CsXp+KWk5U0yHcC8sMgUhjO8g=; b=M/Qc6HsTaL0Amo4MIfavZizMkQIffGkrIhBC7VCTJubxyq4HGyjWecJrHLdeF9DLX/ 8eP74oGs2Wk56cfEfbH5DJ176a08pos5cHCUNYdwpfNdGp01sXRWfAxEE9LufqkGYdQE Bru5qPDTEbctNKA7wwQzCDHwXqt08eoW0aQKnxQIDKuWJtZhevmVdkmDbxPaottsdluF UxDOptrSVOppDWlg8EW6A26xhFDZQgZsUgyhrkogRDQzeKdX1A4CTH3vnAu9KlA3ad5k Yll3ZQFmoSITdgivFdGlpCeozZsNitGPJnUoeBBJu4USEQgSLsYDsdMbP6IiIp90ZdP9 p74w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792182570; x=1792787370; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=1LGZ+rGrPlSG7hr8w6CsXp+KWk5U0yHcC8sMgUhjO8g=; b=kNZZ6xXwd0bORknAZOyIyciV5X9JO8BOos0bsByYrJqxi8ZhAN/yfbh+/B6xDwZ1Hu aPkJEF2B5/Zqz2ven6mqoDtgllDpDyj0wNOzNGw68M2/iWkYWptiAjGMZc/aV7/w9iXy MsiLF/GaCd37gu6up4us1QdYvroxjtJMCl3ZS/3eAn/vTuMDZ9qpRd6GI1NgMOHrIK4j 6tbnt6g/3SEuwLyeddfPz8NJkiQZ4HJ7xImKdGJra7oTZN3ZpGO5KBhdjDg6iNGMo1+U ihDMS595XQZmDFrZbNAk51Cx5eoBeCqn0hazbCh1Q2t+eegi4Ijk/+cBFv21ZPq72JNQ Vh9w== X-Gm-Message-State: AFq9FYKR+5H224S7WMlai9gWrKj/1x8oZfRM1V3/8BcA1mS9zvooyaeh goJdZVDjM5SamV4kiE7bCU+IRbWf3+/b0pqLsqEBcuyrs/UNkPfJAt5vj2V1iqCwUoQ= X-Gm-Gg: AYBFou1PTnpzq7LDoupK1+iWPvWgju8PwBd2e2uHCOEh5kbibtKeE0XWwRI1lJC+Buc q+Vo4rS7uLoPtB6PDxyvSz5q9YV00aj+9O8PU/W4/E3VUIECV06EEUGUcLuqS8Cwcw3ftxphDyc rpTLdxZbsUMW7xbqOwGVzGyn03Sj2TYzyVCIUGwig9Q+s5rN6KGXn6BuY3DhPj3cTngFrpfPSto /p0RULOgpt0w7Ehf3HxjOb+SrZyhBPpo16YsFpU/c97GDSeIvAwFGBvaSOZD+bzcJzWe8PKKPFP aRzBtGaLHqdK7fDO7HMas/0HciBEHZExBaGxDtduYxLQ9zRuP0IVHIFh+8KnATMkQnv+xCQevpv NE0rAJwiils1Sw8+b5lz3LMvA2GSN/7dGtm3uPQ3FHc1tjTaZX9PHKv2FrGW35TMJvtKBOHHixs 3marKKE418P5/m/xb6vhBofsGxU2KcOreFoQrkjwqx/NVFrdblsqtgEvB3rlqI6Hx1qWfaya9Hj BurHPT+yIx1SZM9BXFBPSUid0EI X-Received: by 2002:a05:6102:3f0f:b0:7cc:16e2:c1a8 with SMTP id ada2fe7eead31-7cc3691f500mr290074137.8.1792182570475; Fri, 16 Oct 2026 13:29:30 -0700 (PDT) Received: from localhost ([2601:19c:4a00:897d:3391:ed89:5571:41a8]) by smtp.gmail.com with ESMTPSA id a1e0cc1a2514c-990835c29eesm2271202241.4.2026.10.16.13.29.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Oct 2026 13:29:29 -0700 (PDT) From: Ray Strode To: libcamera-devel@lists.libcamera.org Cc: johannes.goede@oss.qualcomm.com, laurent.pinchart@ideasonboard.com, kieran.bingham@ideasonboard.com Subject: [PATCH 5/7] tests: Validate rendered crops in the integration suite Date: Fri, 16 Oct 2026 16:29:14 -0400 Message-ID: <20261016202916.1405813-6-halfline@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261016202916.1405813-1-halfline@gmail.com> References: <8fd58439-d19e-4539-962f-a6110be5c971@oss.qualcomm.com> <20261016202916.1405813-1-halfline@gmail.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" From: Ray Strode This commit checks rendered software ISP pixels in the opt-in integration test suite. The geometry unit tests check crop calculations. They do not exercise shader uniforms, the viewport or rasterization. The EGL drawing interface allows those parts of the production path to run with CPU-uploaded textures and pixel readback, including on software renderers without DMA-BUF support. The test checks centered horizontal and vertical stripes and compares resized output with a centered crop of the native render. Cases cover two Bayer patterns in 8-bit and packed RAW10 formats at four output sizes. The llvmpipe test setup selects surfaceless Mesa software rendering. An unavailable EGL display fails this setup so CI cannot silently skip the rendering coverage. The normal setup skips when no display is available. Signed-off-by: Ray Strode --- test/integration/meson.build | 2 +- test/integration/software_isp/gpu-crop.cpp | 222 +++++++++++++++++++++ test/integration/software_isp/meson.build | 28 +++ 3 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 test/integration/software_isp/gpu-crop.cpp create mode 100644 test/integration/software_isp/meson.build diff --git a/test/integration/meson.build b/test/integration/meson.build index 51e97101..8a293951 100644 --- a/test/integration/meson.build +++ b/test/integration/meson.build @@ -1,3 +1,3 @@ # SPDX-License-Identifier: CC0-1.0 -# Integration test subdirectories register tests in the 'integration' suite. +subdir('software_isp') diff --git a/test/integration/software_isp/gpu-crop.cpp b/test/integration/software_isp/gpu-crop.cpp new file mode 100644 index 00000000..c327f3a9 --- /dev/null +++ b/test/integration/software_isp/gpu-crop.cpp @@ -0,0 +1,222 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2026, Red Hat Inc. + * + * Software ISP rendered crop tests + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "libcamera/internal/egl.h" +#include "libcamera/internal/software_isp/debayer_params.h" +#include "libcamera/internal/software_isp/swstats_cpu.h" + +#include +#include + +#include "debayer_egl.h" +#include "test.h" + +using namespace libcamera; + +class TextureDebayer : public DebayerEGL +{ +public: + using DebayerEGL::DebayerEGL; + using DebayerEGL::render; +}; + +class GpuCropTest : public Test +{ +protected: + int init() override + { + display_ = eGL::probeDisplay(); + if (display_ == EGL_NO_DISPLAY) { + std::cout << "No surfaceless EGL display available" << std::endl; + return utils::secure_getenv("LIBCAMERA_TEST_REQUIRE_EGL") ? TestFail : TestSkip; + } + + return TestPass; + } + + int run() override + { + CameraManager manager; + for (PixelFormat format : { formats::SBGGR8, formats::SGRBG8, + formats::SBGGR10_CSI2P, formats::SGRBG10_CSI2P }) { + for (bool vertical : { true, false }) { + std::vector reference; + Size native; + for (Size size : { Size(1920, 1080), Size(1280, 720), + Size(640, 480), Size(512, 768) }) { + std::vector image; + if (render(manager, format, size, vertical, &image, &native)) + return TestFail; + + if (!checkCenter(image, size, vertical)) { + std::cerr << format << " " << size + << " stripe is not centered" << std::endl; + return TestFail; + } + + if (reference.empty()) { + if (size != native) + return TestFail; + reference = std::move(image); + continue; + } + + /* Compare with a centered crop of the native render. */ + double scale = std::max(double(size.width) / native.width, + double(size.height) / native.height); + double error = 0; + for (unsigned int y = 0; y < size.height; y++) { + double offsetY = (y + 0.5 - size.height / 2.0) / scale; + int ry = std::lround(native.height / 2.0 + offsetY - 0.5); + for (unsigned int x = 0; x < size.width; x++) { + double offsetX = (x + 0.5 - size.width / 2.0) / scale; + int rx = std::lround(native.width / 2.0 + offsetX - 0.5); + unsigned int actual = (y * size.width + x) * 4; + unsigned int expected = (ry * native.width + rx) * 4; + for (unsigned int c = 0; c < 3; c++) + error += std::abs(int(image[actual + c]) - + reference[expected + c]); + } + } + error /= size.width * size.height * 3; + std::cout << format << " " << size << " " + << (vertical ? "vertical" : "horizontal") + << " stripe: mean pixel error " << error << std::endl; + /* Allow interpolation and rounding at stripe edges. */ + if (error > 2.0) + return TestFail; + } + } + } + + return TestPass; + } + + void cleanup() override + { + eglTerminate(display_); + } + +private: + bool checkCenter(const std::vector &image, const Size &size, bool vertical) + { + unsigned int length = vertical ? size.width : size.height; + double sum = 0; + unsigned int count = 0; + for (unsigned int i = 0; i < length; i++) { + unsigned int x = vertical ? i : size.width / 2; + unsigned int y = vertical ? size.height / 2 : i; + /* The achromatic scene is independent of channel ordering. */ + if (image[(y * size.width + x) * 4 + 1] > 128) { + sum += i + 0.5; + count++; + } + } + if (!count) + return false; + + double center = sum / count; + return std::abs(center - length / 2.0) <= 1.0; + } + + bool createTexture(eGLImage &image, const void *data) + { + glActiveTexture(image.texture_unit_); + glBindTexture(GL_TEXTURE_2D, image.texture_); + glTexImage2D(GL_TEXTURE_2D, 0, image.format_, image.width_, image.height_, + 0, image.format_, GL_UNSIGNED_BYTE, data); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + return glGetError() == GL_NO_ERROR; + } + + int render(CameraManager &manager, PixelFormat format, const Size &size, + bool vertical, std::vector *image, Size *native) + { + auto stats = std::make_unique(manager); + if (!stats->isValid()) + return TestFail; + + TextureDebayer debayer(std::move(stats), manager, display_); + bool packed = format == formats::SBGGR10_CSI2P || format == formats::SGRBG10_CSI2P; + StreamConfiguration input, output; + input.pixelFormat = format; + input.size = { packed ? 1928U : 1924U, 1080 }; + input.stride = debayer.preferredInputStride(format, input.size); + input.bufferCount = 1; + output.pixelFormat = formats::XBGR8888; + output.size = size; + std::tie(output.stride, output.frameSize) = + debayer.strideAndFrameSize(output.pixelFormat, size); + output.bufferCount = 1; + *native = debayer.sizes(format, input.size).max; + if (debayer.configure(input, { output }, false)) + return TestFail; + + /* Destroy the textures before releasing their context. */ + utils::scope_exit stop([&] { debayer.stop(); }); + if (debayer.start()) + return TestFail; + + std::cout << "GL renderer: " << glGetString(GL_RENDERER) << std::endl; + std::vector raw(input.stride * input.size.height, 0); + for (unsigned int y = 0; y < input.size.height; y++) { + for (unsigned int x = 0; x < input.size.width; x++) { + unsigned int coordinate = vertical ? x : y; + unsigned int middle = (vertical ? input.size.width : input.size.height) / 2; + bool bright = coordinate >= middle - 64 && coordinate < middle + 64; + unsigned int offset = packed ? x / 4 * 5 + x % 4 : x; + /* RAW10 stores the high eight bits before the group's low bits. */ + raw[y * input.stride + offset] = bright ? 224 : 32; + } + } + + eGLImage source(GL_LUMINANCE, input.stride, input.size.height, + input.stride, GL_TEXTURE0, 0); + eGLImage target(GL_RGBA, size.width, size.height, output.stride, GL_TEXTURE1, 1); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + if (!createTexture(source, raw.data()) || !createTexture(target, nullptr)) { + std::cerr << "Unable to allocate test textures" << std::endl; + return TestFail; + } + + if (debayer.render(source, target, DebayerParams{})) + return TestFail; + + image->resize(size.width * size.height * 4); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glReadPixels(0, 0, size.width, size.height, GL_RGBA, GL_UNSIGNED_BYTE, image->data()); + if (glGetError() != GL_NO_ERROR) { + std::cerr << "Unable to read rendered pixels" << std::endl; + return TestFail; + } + + return TestPass; + } + + EGLDisplay display_ = EGL_NO_DISPLAY; +}; + +TEST_REGISTER(GpuCropTest) diff --git a/test/integration/software_isp/meson.build b/test/integration/software_isp/meson.build new file mode 100644 index 00000000..2ec17ea0 --- /dev/null +++ b/test/integration/software_isp/meson.build @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: CC0-1.0 + +if not softisp_enabled or not mesa_works + subdir_done() +endif + +add_test_setup('llvmpipe', env : { + 'EGL_PLATFORM': 'surfaceless', + 'GALLIUM_DRIVER': 'llvmpipe', + 'LIBGL_ALWAYS_SOFTWARE': '1', + 'LIBCAMERA_TEST_REQUIRE_EGL': '1', +}) + +software_isp_integration_tests = [ + {'name': 'software-isp-gpu-crop', 'sources': ['gpu-crop.cpp']}, +] + +foreach test : software_isp_integration_tests + exe = executable(test['name'], test['sources'], + dependencies : [libcamera_private, libegl, libglesv2], + implicit_include_directories : false, + link_with : test_libraries, + include_directories : [test_includes_internal, + '../../../src/libcamera/software_isp/']) + + test(test['name'], exe, suite : ['integration', 'software_isp'], + timeout : 120) +endforeach From patchwork Fri Oct 16 20:29:15 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ray Strode X-Patchwork-Id: 28490 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id B07A5C324C for ; Fri, 16 Oct 2026 20:29:37 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 67A2C68E8D; Fri, 16 Oct 2026 22:29:37 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="QolPdtg0"; dkim-atps=neutral Received: from mail-ua1-x936.google.com (mail-ua1-x936.google.com [IPv6:2607:f8b0:4864:20::936]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E0C1D68E82 for ; Fri, 16 Oct 2026 22:29:32 +0200 (CEST) Received: by mail-ua1-x936.google.com with SMTP id a1e0cc1a2514c-982d6efbeadso102778241.0 for ; Fri, 16 Oct 2026 13:29:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792182572; x=1792787372; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=ze+6S/LQa3OwT7S6GxdDe3/AapVYcHNXZHTZzZXKwBA=; b=QolPdtg0HvkwcPnG/up89W8NMcbUgilNDP/7cH666JTh+ry3LxKmhmwKVq5BhZjZ5N DhJzgLGq+x0p0k4w1Wj4jANzkoZJ9ZazWJYud1dcgPL5qFd/E0FLusapPWppZ0J2OgDA E6aaVWsY/Iy6Y8Ysu/gqF6A/3kGNstgX0eQexp8+FX+5VJj2Qjy29jSSIM+da8cz997K DiidX3KLyzbKEtXKlhqq8KHkwWk4OMMJ3h9sp30x9pbdFGg3Kt7V5I4PGhAiMS16xlT4 8gWngmNlbdtZaLKys7l8CPfAo2HAXjoxZnj05X+JVzte4VwzOszllMja39CRdS1OU+ek hDww== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792182572; x=1792787372; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=ze+6S/LQa3OwT7S6GxdDe3/AapVYcHNXZHTZzZXKwBA=; b=B6zYSkxDxjmct2k5BT8CTTQonODo16zf8RzMMW38kavZ8viaenUmVLzXe5EYmc52xF aIyAV1TPhccOmzAN4fUi7gYanmrYQDO0125Kpmn8ORmmWem4VbBnCfFQY+FEUkdPeIxK X6ddeI2gx54gmGfV+/JkNfUTcUDZCgIphCJP9PISuCLO8oNv2qvPe69EEahZJ0euVo+U SZEC/DRkMqG9QTwGjwA08C0SHKCE7ISMivyb/s0LIf1IMJtMAjqrBXim8DT9DUG5Ebu1 j682VYHGHIauSKiMx6PWfwh4fS+h6ZvxD83YXffNNO/sjwfWbG0Wx7ahydlJ2OjgVU3k TwUw== X-Gm-Message-State: AFq9FYLKMJh2dSYgHnSdDms8ihyfvrYFT5NdsylMjuvrOM7+UyLTMY5c 3AdQhzpxrJWF7Oa0l74XKrNKGGj5c+7586LuVEz4Pyzz3mRZJL/tp81ledc+FOgOMl8= X-Gm-Gg: AYBFou3T7aAyae6pJsJtdrrljZmuF5f2r8SuTErrTAJGO6sJJrRXVL+VuutgNqkzDyi xppUpVF11jPBlXSLouNX9uur1RaTcYn+ePqunkdZzelL2e0lD4n0gWGsD2o8xJoXLl5zpJLr6tY Tfm0nd7indaibsZbMl/MwRpHUYfzP0QOKaI1wdR/Qt6RR/YTTlvRoaJeTnVu38ncHZRmairBjQb KDOFstlIvZtM2iVSG7s/G9nFARG3tZ5MZTbxsjXJl11nMyjl9r6cxB+lrn30OR1W+28oJPkbcEY cVKFyIBbLRmq+luRNv+9Rq8zJRP2RsGvL0DqBa88dUleE5HCbfoDTPdurjowXUUdOZlF+JdDjhe uRaT7Z/6c9nEiqk+PLrx9nblHbJwGiGgh4VHEIzdVNU423wnXQxQXkPWxQ8D9cIV/Z8d1vJxQfE KVoTKHloH/DqNgZkxJc7Pz9eCokZ2Px+1Pp3JNIW2tmCWLvrvksloWdeuvMkg8YYfHHOO35vFnW cGJ+JS0PyGZZ7el/CnTF0dZPe8WwtAIpfnI3LU= X-Received: by 2002:a05:6102:3911:b0:7bf:d9dd:82f8 with SMTP id ada2fe7eead31-7cc36a18080mr196941137.21.1792182571653; Fri, 16 Oct 2026 13:29:31 -0700 (PDT) Received: from localhost ([2601:19c:4a00:897d:3391:ed89:5571:41a8]) by smtp.gmail.com with ESMTPSA id a1e0cc1a2514c-990835c29eesm2271202241.4.2026.10.16.13.29.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Oct 2026 13:29:30 -0700 (PDT) From: Ray Strode To: libcamera-devel@lists.libcamera.org Cc: johannes.goede@oss.qualcomm.com, laurent.pinchart@ideasonboard.com, kieran.bingham@ideasonboard.com Subject: [PATCH 6/7] tests: Validate pixel statistics for the visible GPU crop Date: Fri, 16 Oct 2026 16:29:15 -0400 Message-ID: <20261016202916.1405813-7-halfline@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261016202916.1405813-1-halfline@gmail.com> References: <8fd58439-d19e-4539-962f-a6110be5c971@oss.qualcomm.com> <20261016202916.1405813-1-halfline@gmail.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" From: Ray Strode This commit checks the pixels measured by the EGL backend's configured statistics engine in the integration suite. The geometry unit test verifies the expected crop rectangle, but it cannot check that statistics actually sample that region in the original sensor buffer. Synthetic input checks the sample count, symmetric responses within the crop, and pixel inclusion and exclusion at its boundaries. Cases cover two Bayer patterns in 8-bit and packed RAW10 formats at three output sizes. The test configures the EGL backend without creating a rendering context, then processes the sensor buffer with its CPU statistics engine. Signed-off-by: Ray Strode --- test/integration/software_isp/gpu-stats.cpp | 208 ++++++++++++++++++++ test/integration/software_isp/meson.build | 1 + 2 files changed, 209 insertions(+) create mode 100644 test/integration/software_isp/gpu-stats.cpp diff --git a/test/integration/software_isp/gpu-stats.cpp b/test/integration/software_isp/gpu-stats.cpp new file mode 100644 index 00000000..617f9836 --- /dev/null +++ b/test/integration/software_isp/gpu-stats.cpp @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2026, Red Hat Inc. + * + * Software ISP statistics crop integration tests + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "libcamera/internal/mapped_framebuffer.h" +#include "libcamera/internal/shared_mem_object.h" +#include "libcamera/internal/software_isp/swisp_stats.h" +#include "libcamera/internal/software_isp/swstats_cpu.h" + +#include +#include + +#include "debayer_egl.h" +#include "test.h" + +using namespace libcamera; + +class GpuStatisticsTest : public Test +{ +protected: + int run() override + { + CameraManager manager; + /* configure() stores the display without creating a rendering context. */ + EGLDisplay display = eglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA, + EGL_DEFAULT_DISPLAY, nullptr); + if (display == EGL_NO_DISPLAY) + return utils::secure_getenv("LIBCAMERA_TEST_REQUIRE_EGL") ? TestFail : TestSkip; + for (PixelFormat format : { formats::SBGGR8, formats::SGRBG8, + formats::SBGGR10_CSI2P, formats::SGRBG10_CSI2P }) { + bool packed = format == formats::SBGGR10_CSI2P || format == formats::SGRBG10_CSI2P; + bool bggr = format == formats::SBGGR8 || format == formats::SBGGR10_CSI2P; + for (Size outputSize : { Size(1280, 720), Size(640, 480), Size(1920, 1080) }) { + auto statistics = std::make_unique(manager); + SwStatsCpu *stats = statistics.get(); + if (!stats->isValid()) + return TestFail; + DebayerEGL debayer(std::move(statistics), manager, display); + StreamConfiguration input, output; + input.pixelFormat = format; + input.size = { 1928, 1092 }; + input.stride = debayer.preferredInputStride(format, input.size); + input.bufferCount = 4; + output.pixelFormat = formats::XBGR8888; + output.size = outputSize; + output.stride = output.size.width * 4; + output.bufferCount = 4; + if (debayer.configure(input, { output }, false)) + return TestFail; + + void *mapping = mmap(nullptr, sizeof(SwIspStats), PROT_READ, MAP_SHARED, + stats->getStatsFD().get(), 0); + if (mapping == MAP_FAILED) + return TestFail; + utils::scope_exit unmap([&] { + munmap(mapping, sizeof(SwIspStats)); + }); + const auto *shared = static_cast(mapping); + + SharedMem raw("gpu-statistics-test", input.stride * input.size.height); + if (!raw) + return TestFail; + std::fill(raw.mem().begin(), raw.mem().end(), 0); + FrameBuffer::Plane plane; + plane.fd = raw.fd(); + plane.offset = 0; + plane.length = raw.mem().size(); + std::vector planes = { plane }; + FrameBuffer buffer(planes); + MappedFrameBuffer mapped(&buffer, MappedFrameBuffer::MapFlag::Read); + if (!mapped.isValid()) + return TestFail; + + /* Meter the original input buffer, as the GPU debayer does. */ + auto measure = [&](const Rectangle &target) { + auto *pixels = raw.mem().data(); + for (unsigned int y = 0; y < input.size.height; y++) { + for (unsigned int x = 0; x < input.size.width; x++) { + bool green = ((x + y) & 1) == (bggr ? 1 : 0); + bool white = x >= static_cast(target.x) && + x < target.x + target.width && + y >= static_cast(target.y) && + y < target.y + target.height; + unsigned int offset = packed ? x / 4 * 5 + x % 4 : x; + /* Packed RAW10 statistics use the eight high bits. */ + pixels[y * input.stride + offset] = green || white ? 150 : 75; + } + } + stats->processFrame(0, 0, mapped); + return *shared; + }; + + /* + * A 4:3 output sees 1456 sensor columns. A 16:9 output + * sees all native columns, excluding four/eight border + * columns for 8-bit/packed RAW10. The 8-bit top edge at + * row 5 is rounded down to row 4 by the Bayer alignment. + */ + Rectangle visible; + if (outputSize == Size(640, 480)) + visible = { 236, 0, 1456, 1092 }; + else if (packed) + visible = { 4, 6, 1920, 1080 }; + else + visible = { 2, 4, 1924, 1082 }; + uint64_t expectedSamples = uint64_t((visible.width + 3) / 4) * + ((visible.height + 3) / 4); + SwIspStats background = measure({}); + uint64_t samples = std::accumulate(background.yHistogram.begin(), + background.yHistogram.end(), uint64_t{ 0 }); + if (!background.valid || !samples || samples != expectedSamples || + !background.sum_.r() || + !background.sum_.g() || !background.sum_.b()) { + std::cerr << "Invalid background statistics: " << samples + << " samples, expected " << expectedSamples << std::endl; + + return TestFail; + } + + SwIspStats left = measure({ 418, 0, 128, input.size.height }); + SwIspStats right = measure({ 1382, 0, 128, input.size.height }); + if (!left.valid || !right.valid || !left.sum_.g() || !right.sum_.g() || + left.sum_.r() <= background.sum_.r() || + right.sum_.r() <= background.sum_.r() || + left.yHistogram == background.yHistogram || + right.yHistogram == background.yHistogram) { + std::cerr << "No response to pixels inside the visible crop" << std::endl; + + return TestFail; + } + + double leftRatio = double(left.sum_.r()) / left.sum_.g(); + double rightRatio = double(right.sum_.r()) / right.sum_.g(); + std::cout << format << " " << outputSize << " left R/G=" << leftRatio + << " right R/G=" << rightRatio << std::endl; + if (!std::isfinite(leftRatio) || !std::isfinite(rightRatio) || + std::abs(leftRatio - rightRatio) > 0.001 || + left.sum_.g() != right.sum_.g() || + left.sum_.b() != right.sum_.b() || + left.yHistogram != right.yHistogram) { + return TestFail; + } + + int x = visible.x; + int y = visible.y; + int rightEdge = x + visible.width; + int bottomEdge = y + visible.height; + const Rectangle insideRegions[] = { + { x, y, 8, visible.height }, + { rightEdge - 8, y, 8, visible.height }, + { x, y, visible.width, 4 }, + { x, bottomEdge - 4, visible.width, 4 }, + }; + for (Rectangle target : insideRegions) { + SwIspStats inside = measure(target); + if (!inside.valid || inside.sum_.r() <= background.sum_.r() || + inside.sum_.b() <= background.sum_.b() || + inside.yHistogram == background.yHistogram) { + std::cerr << "No response inside crop edge " << target << std::endl; + + return TestFail; + } + } + const Rectangle outsideRegions[] = { + { 0, 0, static_cast(x), input.size.height }, + { rightEdge, 0, input.size.width - rightEdge, input.size.height }, + { 0, 0, input.size.width, static_cast(y) }, + { 0, bottomEdge, input.size.width, input.size.height - bottomEdge }, + }; + for (Rectangle target : outsideRegions) { + if (!target.width || !target.height) + continue; + SwIspStats outside = measure(target); + if (!outside.valid || background.sum_ != outside.sum_ || + background.yHistogram != outside.yHistogram) { + std::cerr << "Metered pixels outside crop edge " << target << std::endl; + + return TestFail; + } + } + } + } + return TestPass; + } +}; + +TEST_REGISTER(GpuStatisticsTest) diff --git a/test/integration/software_isp/meson.build b/test/integration/software_isp/meson.build index 2ec17ea0..0d75f9b7 100644 --- a/test/integration/software_isp/meson.build +++ b/test/integration/software_isp/meson.build @@ -13,6 +13,7 @@ add_test_setup('llvmpipe', env : { software_isp_integration_tests = [ {'name': 'software-isp-gpu-crop', 'sources': ['gpu-crop.cpp']}, + {'name': 'software-isp-gpu-stats', 'sources': ['gpu-stats.cpp']}, ] foreach test : software_isp_integration_tests From patchwork Fri Oct 16 20:29:16 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ray Strode X-Patchwork-Id: 28491 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 4B082C3305 for ; Fri, 16 Oct 2026 20:29:38 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id F292E68E57; Fri, 16 Oct 2026 22:29:37 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="nlogBqOx"; dkim-atps=neutral Received: from mail-ua1-x929.google.com (mail-ua1-x929.google.com [IPv6:2607:f8b0:4864:20::929]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 1CC4568E53 for ; Fri, 16 Oct 2026 22:29:34 +0200 (CEST) Received: by mail-ua1-x929.google.com with SMTP id a1e0cc1a2514c-988bb309c2bso165335241.1 for ; Fri, 16 Oct 2026 13:29:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792182573; x=1792787373; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=Y06AnfNFiMQrdhH9G5M7N8bawbRNE2yyyj6IOBD6Fac=; b=nlogBqOxDSH1w/3hV91o+RGfcFio8JdIAGTckKdcfS+oybpmG8sCg/3YdYzWPV63ZR s+ThQFd9PN+9cWYyt/tt/sVAoIZciOe3kT3uxAdJub3+1KXMx4eDxYe5oyDU6OCjhZ9+ nlpmxGLlqx9b+UkQgEmP8rterFHtP2pbvc3ZCjEo04mpFuW4RLIVH/Z/ovGU3dME4laC +mQQZ99MFfdI1d+n3RuDT9wfSqEEB4yIi8mr0DYFDOihj7YkAxe5G919Y5QQIwU/9iPe c6YmGAOisL005Iptb4uvIvHt5693/joH/nF4owGkazutmvBRpIWNHuemLzOC1ypLuMkB K84A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792182573; x=1792787373; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=Y06AnfNFiMQrdhH9G5M7N8bawbRNE2yyyj6IOBD6Fac=; b=wAfxRU7o5Wc2Lib2jIgK+HWsoAD+9Cur3mtl7j79k4oeBVYplXG95GQJrotmJKG/Sm pSUh5HMx1bqQz7deR1NAbnsKkQRMREUvDx47fQDOkwJDKxyvSTLWgh57mmhGJ/XG9pLO xmYJRsn7uCKaBu1MDQ+JO6e93eO692LOXg9DaACIHzlxwXuNOtiJ5+vgjllMn46Gi2hR vHvGajtGhs9s5uho0aW4LwASU5+vaUIoeExCqLyZulAz597so+DECYy9d26A/CaUs6aB QEwu17XqXsk6Sx9+l/MoUqpeieT4G9QUIGa1aEbxFKNC4gmMt8t1Ah+6UVsx++c2muPJ eOeA== X-Gm-Message-State: AFq9FYIXQ3kho44RC+zPn+/1JjiRb/STqDDkWNI5zYTqYLUufICyctdF OTZsaVqaFFCE+sViFNGrQLb1WLjKHO6PxyyqAqOGgVdff0MK9DQoFnsOWAvXktpyrZY= X-Gm-Gg: AYBFou2SIktyQi+AQGsuZnvgucJw43zpPeAxZffqvbWueBvo4UTARwFNOTwiqlSbFBb nZMfhbzYngfrCHmbP+TJDAzizq5e7K4tER2bJrMhaIstyf+U/2bQKxPC4wh2P8NPuxcEFX25ONW lyP6qjrz/kf6lh9CacT6h+LJNmNYhSPUhVuhLgGFzSfnICv6vuYsCrbaQSE9BlwltkpSkWz1afM LUsAkyzl62/UYmknXhX4db5d0hzo7P6WB14iWb5+9BxkbA/xG7+SeHFWr9rvoXDqirWn+TE8s6P ZKY7fiJSgexzIeOTheS3iUlh0X6HTI7CcigPtpmOHNArOJdorNqR88ZKnPETalQ+E6lGOr32sVm 6bH6psStPz4Ojtz/8vue4t4hegfJZ7Fq0TdJnot7cQtkEVau3XHWVHt/1XZUjPT0LBJ1e+j5Dui KYEwCkyqXnRVZnclQH8rKF0YrHKuGeb60n4MvWAxyes7q4LXuTFYPuYLY5DpLkMQwVlftu1zokk mbQveT7k+13MVU9uV+cYfB/+1wc X-Received: by 2002:a05:6102:955:b0:7cb:fd69:4155 with SMTP id ada2fe7eead31-7cc3672eb71mr301443137.5.1792182572918; Fri, 16 Oct 2026 13:29:32 -0700 (PDT) Received: from localhost ([2601:19c:4a00:897d:3391:ed89:5571:41a8]) by smtp.gmail.com with ESMTPSA id a1e0cc1a2514c-990835c29eesm2271202241.4.2026.10.16.13.29.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Oct 2026 13:29:32 -0700 (PDT) From: Ray Strode To: libcamera-devel@lists.libcamera.org Cc: johannes.goede@oss.qualcomm.com, laurent.pinchart@ideasonboard.com, kieran.bingham@ideasonboard.com Subject: [PATCH 7/7] docs: Describe software ISP integration testing Date: Fri, 16 Oct 2026 16:29:16 -0400 Message-ID: <20261016202916.1405813-8-halfline@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261016202916.1405813-1-halfline@gmail.com> References: <8fd58439-d19e-4539-962f-a6110be5c971@oss.qualcomm.com> <20261016202916.1405813-1-halfline@gmail.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" From: Ray Strode This commit documents the software ISP integration tests and the llvmpipe setup for running them without camera or GPU hardware. The guide describes the rendered-pixel and configured statistics checks, their build dependencies and runtime skips, and the stricter software-rendering setup. The software_isp component suite selects these checks independently of the existing device integration tests. Buffer import, synchronization, camera capture and completed-request metadata remain outside the software ISP checks described here. Signed-off-by: Ray Strode --- Documentation/testing.rst | 53 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/Documentation/testing.rst b/Documentation/testing.rst index 49c1f085..26c590ff 100644 --- a/Documentation/testing.rst +++ b/Documentation/testing.rst @@ -33,3 +33,56 @@ set ``-Dtest=false`` as well as enabling the desired group. Many integration tests need the kernel's vimc or vivid test devices. Integration builds automatically include the vimc pipeline handler and its IPA. Unit-only selection does not add that pipeline. + +Software ISP integration tests +------------------------------ + +The software ISP integration tests require the simple pipeline handler and +EGL/GLES support. They generate their input in memory and require neither a +camera nor DMA-BUF allocation or import support. + +.. code-block:: shell + + meson setup build-integration -Dpipelines=simple -Dipas=softisp \ + -Dsoftisp-gpu=enabled -Dtest-integration=true + ninja -C build-integration + meson test -C build-integration --suite integration --print-errorlogs + +``software-isp-gpu-crop`` renders synthetic Bayer frames through the software +ISP's shader setup and texture drawing path. It checks centered horizontal +and vertical stripes, then compares resized outputs with a centered crop of +the native render. Cases cover 8-bit and packed RAW10 input with two Bayer +patterns and four output sizes. + +``software-isp-gpu-stats`` configures the EGL backend and processes synthetic +pixels with its CPU statistics engine. It checks sample counts, symmetry, +and inclusion and exclusion of pixels at the configured crop boundaries. +This test does not create a rendering context. + +These checks cover rendering and statistics selection. Buffer import, +synchronization, camera capture and completed-request metadata require +additional integration tests. + +The ``software_isp`` component suite selects these two checks separately +from the other integration tests. + +Software rendering in CI +------------------------ + +Mesa's llvmpipe driver can run these tests without a GPU. The ``llvmpipe`` +Meson test setup selects surfaceless EGL and forces Mesa software rendering: + +.. code-block:: shell + + meson test -C build-integration --suite software_isp --setup llvmpipe \ + --print-errorlogs + +The runner needs Mesa EGL/GLES libraries and the llvmpipe driver. The crop +test prints its GL renderer so CI logs identify the implementation in use. +An unavailable EGL display is a failure with this setup, preventing missing +rendering coverage from being reported as a successful skipped test. + +With the normal setup, an unavailable EGL display produces a test skip. +Shader compilation, rendering, pixel comparisons and statistics mismatches +are failures in either setup. The software ISP integration tests are not +registered when the software ISP or its EGL backend is disabled at build time.