Patch Detail
Show a patch.
GET /api/patches/27121/?format=api
{ "id": 27121, "url": "https://patchwork.libcamera.org/api/patches/27121/?format=api", "web_url": "https://patchwork.libcamera.org/patch/27121/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/projects/1/?format=api", "name": "libcamera", "link_name": "libcamera", "list_id": "libcamera_core", "list_email": "libcamera-devel@lists.libcamera.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20260629163017.863145-43-barnabas.pocze@ideasonboard.com>", "date": "2026-06-29T16:30:05", "name": "[RFC,v1,42/54] apps: lc-compliance: Add buffer pool tests", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "7e9162629c833135a41747dc619e7bf8e0d201a2", "submitter": { "id": 216, "url": "https://patchwork.libcamera.org/api/people/216/?format=api", "name": "Barnabás Pőcze", "email": "barnabas.pocze@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/27121/mbox/", "series": [ { "id": 6025, "url": "https://patchwork.libcamera.org/api/series/6025/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=6025", "date": "2026-06-29T16:29:23", "name": "libcamera: Split requests and buffers", "version": 1, "mbox": "https://patchwork.libcamera.org/series/6025/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/27121/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/27121/checks/", "tags": {}, "headers": { "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>", "X-Original-To": "parsemail@patchwork.libcamera.org", "Delivered-To": "parsemail@patchwork.libcamera.org", "Received": [ "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id CBF49C3261\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 29 Jun 2026 16:31:34 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 63CC065FB4;\n\tMon, 29 Jun 2026 18:31:34 +0200 (CEST)", "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 86F4865F67\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 29 Jun 2026 18:30:30 +0200 (CEST)", "from pb-laptop.local (185.221.140.128.nat.pool.zt.hu\n\t[185.221.140.128])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 6203B1044\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 29 Jun 2026 18:29:47 +0200 (CEST)" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"HCUbhBuG\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1782750587;\n\tbh=XDpIfMwN9sk/4wblvqoDdLcgCYGwYnvaFy8Y9/2q/aQ=;\n\th=From:To:Subject:Date:In-Reply-To:References:From;\n\tb=HCUbhBuGCYomCT8qkihyY+W5rLjYaSA9N51gD/ZLEvmGDprCUEGuZwXwwWfD8YFvJ\n\t9RK1E4KMtXALzvMTtuKn4Q1jVrJeN7iQFuzld96FJV34+cTzLHM9JoYh3wPZws03GV\n\t1klJQQ4sO/MNwby0GE/Gxh+ZgZ6va17rHWzQhCHM=", "From": "=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org", "Subject": "[RFC PATCH v1 42/54] apps: lc-compliance: Add buffer pool tests", "Date": "Mon, 29 Jun 2026 18:30:05 +0200", "Message-ID": "<20260629163017.863145-43-barnabas.pocze@ideasonboard.com>", "X-Mailer": "git-send-email 2.54.0", "In-Reply-To": "<20260629163017.863145-1-barnabas.pocze@ideasonboard.com>", "References": "<20260629163017.863145-1-barnabas.pocze@ideasonboard.com>", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=UTF-8", "Content-Transfer-Encoding": "8bit", "X-BeenThere": "libcamera-devel@lists.libcamera.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "<libcamera-devel.lists.libcamera.org>", "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>", "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>", "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>", "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>", "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "Add a couple tests that test the functioning of the buffer pool of a camera.\n\nSigned-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n---\n src/apps/lc-compliance/tests/capture_test.cpp | 261 ++++++++++++++++++\n 1 file changed, 261 insertions(+)", "diff": "diff --git a/src/apps/lc-compliance/tests/capture_test.cpp b/src/apps/lc-compliance/tests/capture_test.cpp\nindex d7d6f0626e..f256ec3033 100644\n--- a/src/apps/lc-compliance/tests/capture_test.cpp\n+++ b/src/apps/lc-compliance/tests/capture_test.cpp\n@@ -8,8 +8,10 @@\n \n #include \"capture.h\"\n \n+#include <semaphore>\n #include <sstream>\n #include <string>\n+#include <thread>\n #include <tuple>\n #include <vector>\n \n@@ -142,4 +144,263 @@ INSTANTIATE_TEST_SUITE_P(MultiStream,\n \t\t\t\t\t testing::ValuesIn(NUMREQUESTS)),\n \t\t\t SimpleCapture::nameParameters);\n \n+class TestWithCamera : public testing::Test, public CameraHolder\n+{\n+protected:\n+\tvoid SetUp() override { acquireCamera(); }\n+\tvoid TearDown() override { releaseCamera(); }\n+};\n+\n+class BufferPool : public TestWithCamera\n+{\n+protected:\n+\tvoid SetUp() override\n+\t{\n+\t\tTestWithCamera::SetUp();\n+\n+\t\tconfig_ = camera_->generateConfiguration({ StreamRole::Viewfinder });\n+\t\tASSERT_TRUE(config_);\n+\t\tASSERT_FALSE(config_->empty());\n+\n+\t\tASSERT_EQ(camera_->configure(config_.get()), 0);\n+\t}\n+\n+\tvoid TearDown() override\n+\t{\n+\t\tconfig_.reset();\n+\t\tTestWithCamera::TearDown();\n+\t}\n+\n+\tstd::vector<std::unique_ptr<Request>>\n+\tcreateRequests(std::size_t count)\n+\t{\n+\t\tstd::vector<std::unique_ptr<Request>> requests;\n+\n+\t\tfor (std::size_t i = 0; i < count; i++) {\n+\t\t\tauto request = camera_->createRequest(i);\n+\t\t\t[&] { ASSERT_TRUE(request); }();\n+\n+\t\t\tfor (const auto &cfg : *config_)\n+\t\t\t\trequest->enableStream(cfg.stream(), true);\n+\n+\t\t\trequests.push_back(std::move(request));\n+\t\t}\n+\n+\t\treturn requests;\n+\t}\n+\n+\tstd::unique_ptr<CameraConfiguration> config_;\n+};\n+\n+template<typename T, typename... Args>\n+struct ScopedSignalReceiver {\n+\tSignal<Args...> &signal;\n+\tT *object = nullptr;\n+\n+\ttemplate<typename Func>\n+\tScopedSignalReceiver(Signal<Args...> &s, T *o, Func f)\n+\t\t: signal(s), object(o)\n+\t{\n+\t\tsignal.connect(o, std::move(f));\n+\t}\n+\n+\t~ScopedSignalReceiver()\n+\t{\n+\t\tsignal.disconnect(object);\n+\t}\n+};\n+\n+template<typename T, typename... Args>\n+ScopedSignalReceiver(Signal<Args...> &, T *) -> ScopedSignalReceiver<T, Args...>;\n+\n+struct ScopedCameraStart {\n+\tCamera &camera;\n+\n+\tScopedCameraStart(Camera &c)\n+\t\t: camera(c)\n+\t{\n+\t\t[&]() { ASSERT_EQ(camera.start(), 0); }();\n+\t}\n+\n+\t~ScopedCameraStart()\n+\t{\n+\t\tEXPECT_EQ(camera.stop(), 0);\n+\t}\n+};\n+\n+TEST_F(BufferPool, BufferBeforeRequest)\n+{\n+\tFrameBufferAllocator fba(camera_);\n+\n+\tstd::size_t buffers = -1;\n+\tfor (const auto &cfg : *config_) {\n+\t\tASSERT_GT(fba.allocate(cfg.stream()), 0);\n+\t\tbuffers = std::min(buffers, fba.buffers(cfg.stream()).size());\n+\t}\n+\n+\tauto requests = createRequests(buffers);\n+\n+\tstd::counting_semaphore<> requestCompletedSemaphore(0);\n+\tbool requestCompletedOk = true;\n+\tunsigned int requestCompleted = 0;\n+\tScopedSignalReceiver rcr(camera_->requestCompleted, this, [&](Request *request) {\n+\t\tif (requestCompleted < requests.size())\n+\t\t\trequestCompletedOk &= request == requests[requestCompleted].get();\n+\n+\t\trequestCompletedSemaphore.release(1);\n+\t\trequestCompleted += 1;\n+\t});\n+\n+\t{\n+\t\tScopedCameraStart scs(*camera_);\n+\n+\t\tfor (const auto &cfg : *config_) {\n+\t\t\tStream *stream = cfg.stream();\n+\t\t\tfor (const auto &buffer : fba.buffers(stream))\n+\t\t\t\tEXPECT_EQ(camera_->addBuffer(stream, buffer.get()), 0);\n+\t\t}\n+\n+\t\tfor (const auto &request : requests)\n+\t\t\tASSERT_EQ(camera_->queueRequest(request.get()), 0);\n+\n+\t\tauto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(buffers);\n+\n+\t\tfor (size_t i = 0; i < buffers; i++) {\n+\t\t\tif (!requestCompletedSemaphore.try_acquire_until(deadline))\n+\t\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tEXPECT_TRUE(requestCompletedOk);\n+\tASSERT_FALSE(requestCompletedSemaphore.try_acquire());\n+\tEXPECT_EQ(requestCompleted, buffers);\n+}\n+\n+TEST_F(BufferPool, RequestBeforeBuffer)\n+{\n+\tFrameBufferAllocator fba(camera_);\n+\n+\tstd::size_t buffers = -1;\n+\tfor (const auto &cfg : *config_) {\n+\t\tASSERT_GT(fba.allocate(cfg.stream()), 0);\n+\t\tbuffers = std::min(buffers, fba.buffers(cfg.stream()).size());\n+\t}\n+\n+\tauto requests = createRequests(buffers);\n+\n+\tstd::counting_semaphore<> requestCompletedSemaphore(0);\n+\tbool requestCompletedOk = true;\n+\tunsigned int requestCompleted = 0;\n+\tScopedSignalReceiver rcr(camera_->requestCompleted, this, [&](Request *request) {\n+\t\tif (requestCompleted < requests.size())\n+\t\t\trequestCompletedOk &= request == requests[requestCompleted].get();\n+\n+\t\trequestCompletedSemaphore.release(1);\n+\t\trequestCompleted += 1;\n+\t});\n+\n+\t{\n+\t\tScopedCameraStart scs(*camera_);\n+\n+\t\tfor (const auto &request : requests)\n+\t\t\tASSERT_EQ(camera_->queueRequest(request.get()), 0);\n+\n+\t\tstd::this_thread::sleep_for(std::chrono::seconds(2));\n+\t\tASSERT_FALSE(requestCompletedSemaphore.try_acquire());\n+\t\tASSERT_EQ(requestCompleted, 0);\n+\n+\t\tauto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(buffers);\n+\n+\t\tfor (const auto &cfg : *config_) {\n+\t\t\tStream *stream = cfg.stream();\n+\t\t\tfor (const auto &buffer : fba.buffers(stream))\n+\t\t\t\tEXPECT_EQ(camera_->addBuffer(stream, buffer.get()), 0);\n+\t\t}\n+\n+\t\tfor (size_t i = 0; i < buffers; i++) {\n+\t\t\tif (!requestCompletedSemaphore.try_acquire_until(deadline))\n+\t\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tEXPECT_TRUE(requestCompletedOk);\n+\tASSERT_FALSE(requestCompletedSemaphore.try_acquire());\n+\tEXPECT_EQ(requestCompleted, buffers);\n+}\n+\n+TEST_F(BufferPool, BufferWithoutRequest)\n+{\n+\tFrameBufferAllocator fba(camera_);\n+\tstd::set<std::pair<const Stream *, FrameBuffer *>> buffers;\n+\n+\tfor (const auto &cfg : *config_) {\n+\t\tStream *stream = cfg.stream();\n+\t\tASSERT_GT(fba.allocate(stream), 0);\n+\n+\t\tfor (const auto &buffer : fba.buffers(stream))\n+\t\t\tbuffers.insert({ stream, buffer.get() });\n+\t}\n+\n+\tunsigned int requestCompleted = 0;\n+\tScopedSignalReceiver rcr(camera_->requestCompleted, this, [&](Request *) {\n+\t\trequestCompleted += 1;\n+\t});\n+\n+\tbool bufferCompletedOk = true;\n+\tScopedSignalReceiver bcr(camera_->bufferCompleted, this, [&](Request *request, const Stream *stream, FrameBuffer *buffer) {\n+\t\tbufferCompletedOk &= !request;\n+\t\tbufferCompletedOk &= buffer->metadata().status == libcamera::FrameMetadata::FrameCancelled;\n+\t\tbufferCompletedOk &= buffers.erase({ stream, buffer });\n+\t});\n+\n+\t{\n+\t\tScopedCameraStart scs(*camera_);\n+\n+\t\tfor (const auto &cfg : *config_) {\n+\t\t\tStream *stream = cfg.stream();\n+\t\t\tfor (const auto &buffer : fba.buffers(stream))\n+\t\t\t\tEXPECT_EQ(camera_->addBuffer(stream, buffer.get()), 0);\n+\t\t}\n+\t}\n+\n+\tEXPECT_EQ(requestCompleted, 0);\n+\tEXPECT_EQ(buffers.size(), 0);\n+\tEXPECT_TRUE(bufferCompletedOk);\n+}\n+\n+TEST_F(BufferPool, RequestWithoutBuffers)\n+{\n+\tconstexpr std::size_t kRequestCount = 128;\n+\tauto requests = createRequests(kRequestCount);\n+\n+\tunsigned int requestCompleted = 0;\n+\tbool requestCompletedOk = true;\n+\tScopedSignalReceiver rcr(camera_->requestCompleted, this, [&](Request *request) {\n+\t\tif (requestCompleted < requests.size()) {\n+\t\t\trequestCompletedOk &= request == requests[requestCompleted].get();\n+\t\t\trequestCompletedOk &= request->status() == Request::Status::RequestCancelled;\n+\t\t}\n+\n+\t\trequestCompleted += 1;\n+\t});\n+\n+\tunsigned int bufferCompleted = 0;\n+\tScopedSignalReceiver bcr(camera_->bufferCompleted, this, [&](Request *, const Stream *, FrameBuffer *) {\n+\t\tbufferCompleted += 1;\n+\t});\n+\n+\t{\n+\t\tScopedCameraStart scs(*camera_);\n+\n+\t\tfor (const auto &request : requests)\n+\t\t\tASSERT_EQ(camera_->queueRequest(request.get()), 0);\n+\n+\t\tstd::this_thread::sleep_for(std::chrono::seconds(2));\n+\t}\n+\n+\tEXPECT_EQ(requestCompleted, requests.size());\n+\tEXPECT_TRUE(requestCompletedOk);\n+\tEXPECT_EQ(bufferCompleted, 0);\n+}\n+\n } /* namespace */\n", "prefixes": [ "RFC", "v1", "42/54" ] }