Show a patch.

GET /api/1.1/patches/10663/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 10663,
    "url": "https://patchwork.libcamera.org/api/1.1/patches/10663/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/10663/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/1.1/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": "<20201215004811.602429-3-niklas.soderlund@ragnatech.se>",
    "date": "2020-12-15T00:48:05",
    "name": "[libcamera-devel,v4,2/8] test: delayed_controls: Add test case for DelayedControls",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "7da699c3fb2937a630cc493f49843e758e0636ed",
    "submitter": {
        "id": 5,
        "url": "https://patchwork.libcamera.org/api/1.1/people/5/?format=api",
        "name": "Niklas Söderlund",
        "email": "niklas.soderlund@ragnatech.se"
    },
    "delegate": {
        "id": 16,
        "url": "https://patchwork.libcamera.org/api/1.1/users/16/?format=api",
        "username": "neg",
        "first_name": "Niklas",
        "last_name": "Söderlund",
        "email": "niklas.soderlund@ragnatech.se"
    },
    "mbox": "https://patchwork.libcamera.org/patch/10663/mbox/",
    "series": [
        {
            "id": 1533,
            "url": "https://patchwork.libcamera.org/api/1.1/series/1533/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=1533",
            "date": "2020-12-15T00:48:03",
            "name": "libcamera: Add helper for controls that take effect with a delay",
            "version": 4,
            "mbox": "https://patchwork.libcamera.org/series/1533/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/10663/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/10663/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 6F14ABD80A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 15 Dec 2020 00:48:25 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 4B80461598;\n\tTue, 15 Dec 2020 01:48:25 +0100 (CET)",
            "from bin-mail-out-05.binero.net (bin-mail-out-05.binero.net\n\t[195.74.38.228])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id C33EA61590\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 15 Dec 2020 01:48:22 +0100 (CET)",
            "from bismarck.berto.se (p4fca2458.dip0.t-ipconnect.de\n\t[79.202.36.88])\n\tby bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA\n\tid 3a4aa245-3e6f-11eb-a076-005056917f90;\n\tTue, 15 Dec 2020 01:48:21 +0100 (CET)"
        ],
        "X-Halon-ID": "3a4aa245-3e6f-11eb-a076-005056917f90",
        "Authorized-sender": "niklas.soderlund@fsdn.se",
        "From": "=?utf-8?q?Niklas_S=C3=B6derlund?= <niklas.soderlund@ragnatech.se>",
        "To": "libcamera-devel@lists.libcamera.org,\n\tnaush@raspberrypi.com",
        "Date": "Tue, 15 Dec 2020 01:48:05 +0100",
        "Message-Id": "<20201215004811.602429-3-niklas.soderlund@ragnatech.se>",
        "X-Mailer": "git-send-email 2.29.2",
        "In-Reply-To": "<20201215004811.602429-1-niklas.soderlund@ragnatech.se>",
        "References": "<20201215004811.602429-1-niklas.soderlund@ragnatech.se>",
        "MIME-Version": "1.0",
        "Subject": "[libcamera-devel] [PATCH v4 2/8] test: delayed_controls: Add test\n\tcase for DelayedControls",
        "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>",
        "Content-Type": "text/plain; charset=\"utf-8\"",
        "Content-Transfer-Encoding": "base64",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "Add a test-case for DelayedControls that exercise the setting of\ncontrols and reading back what controls were used for a particular\nframe. Also exercise corner case such as a V4L2 devices that do not\nreset its sequence number to 0 at stream on and sequence number wrapping\naround the uint32_t value space.\n\nSigned-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\nReviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n---\n* Changes since v3\n- Update commit message.\n- Update header documentation.\n- Add protected and override.\n- Use unsigned int instead of int32_t in loops.\n\n* Changes since v2\n- Remove a unused LoC.\n- Add and remove blank lines.\n- Align number of loop iterations.\n---\n test/delayed_contols.cpp | 304 +++++++++++++++++++++++++++++++++++++++\n test/meson.build         |   1 +\n 2 files changed, 305 insertions(+)\n create mode 100644 test/delayed_contols.cpp",
    "diff": "diff --git a/test/delayed_contols.cpp b/test/delayed_contols.cpp\nnew file mode 100644\nindex 0000000000000000..0a2b47aebf06b7fb\n--- /dev/null\n+++ b/test/delayed_contols.cpp\n@@ -0,0 +1,304 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2020, Google Inc.\n+ *\n+ * delayed_controls.cpp - libcamera delayed controls test\n+ */\n+\n+#include <iostream>\n+\n+#include \"libcamera/internal/delayed_controls.h\"\n+#include \"libcamera/internal/device_enumerator.h\"\n+#include \"libcamera/internal/media_device.h\"\n+#include \"libcamera/internal/v4l2_videodevice.h\"\n+\n+#include \"test.h\"\n+\n+using namespace std;\n+using namespace libcamera;\n+\n+class DelayedControlsTest : public Test\n+{\n+public:\n+\tDelayedControlsTest()\n+\t\t: dev_(nullptr)\n+\t{\n+\t}\n+\n+protected:\n+\tint init() override\n+\t{\n+\t\tenumerator_ = DeviceEnumerator::create();\n+\t\tif (!enumerator_) {\n+\t\t\tcerr << \"Failed to create device enumerator\" << endl;\n+\t\t\treturn TestFail;\n+\t\t}\n+\n+\t\tif (enumerator_->enumerate()) {\n+\t\t\tcerr << \"Failed to enumerate media devices\" << endl;\n+\t\t\treturn TestFail;\n+\t\t}\n+\n+\t\tDeviceMatch dm(\"vivid\");\n+\t\tdm.add(\"vivid-000-vid-cap\");\n+\n+\t\tmedia_ = enumerator_->search(dm);\n+\t\tif (!media_) {\n+\t\t\tcerr << \"vivid video device found\" << endl;\n+\t\t\treturn TestSkip;\n+\t\t}\n+\n+\t\tMediaEntity *entity = media_->getEntityByName(\"vivid-000-vid-cap\");\n+\t\tdev_ = new V4L2VideoDevice(entity->deviceNode());\n+\t\tif (dev_->open()) {\n+\t\t\tcerr << \"Failed to open video device\" << endl;\n+\t\t\treturn TestFail;\n+\t\t}\n+\n+\t\tconst ControlInfoMap &infoMap = dev_->controls();\n+\n+\t\t/* Test control enumeration. */\n+\t\tif (infoMap.empty()) {\n+\t\t\tcerr << \"Failed to enumerate controls\" << endl;\n+\t\t\treturn TestFail;\n+\t\t}\n+\n+\t\tif (infoMap.find(V4L2_CID_BRIGHTNESS) == infoMap.end() ||\n+\t\t    infoMap.find(V4L2_CID_CONTRAST) == infoMap.end()) {\n+\t\t\tcerr << \"Missing controls\" << endl;\n+\t\t\treturn TestFail;\n+\t\t}\n+\n+\t\treturn TestPass;\n+\t}\n+\n+\tint singleControlNoDelay()\n+\t{\n+\t\tstd::unordered_map<uint32_t, unsigned int> delays = {\n+\t\t\t{ V4L2_CID_BRIGHTNESS, 0 },\n+\t\t};\n+\t\tstd::unique_ptr<DelayedControls> delayed =\n+\t\t\tstd::make_unique<DelayedControls>(dev_, delays);\n+\t\tControlList ctrls;\n+\n+\t\t/* Reset control to value not used in test. */\n+\t\tctrls.set(V4L2_CID_BRIGHTNESS, 1);\n+\t\tdev_->setControls(&ctrls);\n+\n+\t\t/* Test control without delay are set at once. */\n+\t\tfor (int32_t i = 0; i < 100; i++) {\n+\t\t\tint32_t value = 100 + i;\n+\n+\t\t\tctrls.set(V4L2_CID_BRIGHTNESS, value);\n+\t\t\tdelayed->push(ctrls);\n+\n+\t\t\tdelayed->applyControls(i);\n+\n+\t\t\tControlList result = delayed->get(i);\n+\t\t\tint32_t brightness = result.get(V4L2_CID_BRIGHTNESS).get<int32_t>();\n+\t\t\tif (brightness != value) {\n+\t\t\t\tcerr << \"Failed single control without delay\"\n+\t\t\t\t     << \" frame \" << i\n+\t\t\t\t     << \" expected \" << value\n+\t\t\t\t     << \" got \" << brightness\n+\t\t\t\t     << endl;\n+\t\t\t\treturn TestFail;\n+\t\t\t}\n+\t\t}\n+\n+\t\treturn TestPass;\n+\t}\n+\n+\tint singleControlWithDelay()\n+\t{\n+\t\tstd::unordered_map<uint32_t, unsigned int> delays = {\n+\t\t\t{ V4L2_CID_BRIGHTNESS, 1 },\n+\t\t};\n+\t\tstd::unique_ptr<DelayedControls> delayed =\n+\t\t\tstd::make_unique<DelayedControls>(dev_, delays);\n+\t\tControlList ctrls;\n+\n+\t\t/* Reset control to value that will be first in test. */\n+\t\tint32_t expected = 4;\n+\t\tctrls.set(V4L2_CID_BRIGHTNESS, expected);\n+\t\tdev_->setControls(&ctrls);\n+\t\tdelayed->reset();\n+\n+\t\t/* Test single control with delay. */\n+\t\tfor (int32_t i = 0; i < 100; i++) {\n+\t\t\tint32_t value = 10 + i;\n+\n+\t\t\tctrls.set(V4L2_CID_BRIGHTNESS, value);\n+\t\t\tdelayed->push(ctrls);\n+\n+\t\t\tdelayed->applyControls(i);\n+\n+\t\t\tControlList result = delayed->get(i);\n+\t\t\tint32_t brightness = result.get(V4L2_CID_BRIGHTNESS).get<int32_t>();\n+\t\t\tif (brightness != expected) {\n+\t\t\t\tcerr << \"Failed single control with delay\"\n+\t\t\t\t     << \" frame \" << i\n+\t\t\t\t     << \" expected \" << expected\n+\t\t\t\t     << \" got \" << brightness\n+\t\t\t\t     << endl;\n+\t\t\t\treturn TestFail;\n+\t\t\t}\n+\n+\t\t\texpected = value;\n+\t\t}\n+\n+\t\treturn TestPass;\n+\t}\n+\n+\tint dualControlsWithDelay(uint32_t startOffset)\n+\t{\n+\t\tstd::unordered_map<uint32_t, unsigned int> delays = {\n+\t\t\t{ V4L2_CID_BRIGHTNESS, 1 },\n+\t\t\t{ V4L2_CID_CONTRAST, 2 },\n+\t\t};\n+\t\tstd::unique_ptr<DelayedControls> delayed =\n+\t\t\tstd::make_unique<DelayedControls>(dev_, delays);\n+\t\tControlList ctrls;\n+\n+\t\t/* Reset control to value that will be first two frames in test. */\n+\t\tint32_t expected = 200;\n+\t\tctrls.set(V4L2_CID_BRIGHTNESS, expected);\n+\t\tctrls.set(V4L2_CID_CONTRAST, expected);\n+\t\tdev_->setControls(&ctrls);\n+\t\tdelayed->reset();\n+\n+\t\t/* Test dual control with delay. */\n+\t\tfor (int32_t i = 0; i < 100; i++) {\n+\t\t\tuint32_t frame = startOffset + i;\n+\t\t\tint32_t value = 10 + i;\n+\n+\t\t\tctrls.set(V4L2_CID_BRIGHTNESS, value);\n+\t\t\tctrls.set(V4L2_CID_CONTRAST, value);\n+\t\t\tdelayed->push(ctrls);\n+\n+\t\t\tdelayed->applyControls(frame);\n+\n+\t\t\tControlList result = delayed->get(frame);\n+\t\t\tint32_t brightness = result.get(V4L2_CID_BRIGHTNESS).get<int32_t>();\n+\t\t\tint32_t contrast = result.get(V4L2_CID_CONTRAST).get<int32_t>();\n+\t\t\tif (brightness != expected || contrast != expected) {\n+\t\t\t\tcerr << \"Failed dual controls\"\n+\t\t\t\t     << \" frame \" << frame\n+\t\t\t\t     << \" brightness \" << brightness\n+\t\t\t\t     << \" contrast \" << contrast\n+\t\t\t\t     << \" expected \" << expected\n+\t\t\t\t     << endl;\n+\t\t\t\treturn TestFail;\n+\t\t\t}\n+\n+\t\t\texpected = i < 1 ? expected : value - 1;\n+\t\t}\n+\n+\t\treturn TestPass;\n+\t}\n+\n+\tint dualControlsMultiQueue()\n+\t{\n+\t\tstd::unordered_map<uint32_t, unsigned int> delays = {\n+\t\t\t{ V4L2_CID_BRIGHTNESS, 1 },\n+\t\t\t{ V4L2_CID_CONTRAST, 2 },\n+\t\t};\n+\t\tstd::unique_ptr<DelayedControls> delayed =\n+\t\t\tstd::make_unique<DelayedControls>(dev_, delays);\n+\t\tControlList ctrls;\n+\n+\t\t/* Reset control to value that will be first two frames in test. */\n+\t\tint32_t expected = 100;\n+\t\tctrls.set(V4L2_CID_BRIGHTNESS, expected);\n+\t\tctrls.set(V4L2_CID_CONTRAST, expected);\n+\t\tdev_->setControls(&ctrls);\n+\t\tdelayed->reset();\n+\n+\t\t/*\n+\t\t * Queue all controls before any fake frame start. Note we\n+\t\t * can't queue up more then the delayed controls history size\n+\t\t * which is 16. Where one spot is used by the reset control.\n+\t\t */\n+\t\tfor (unsigned int i = 0; i < 15; i++) {\n+\t\t\tint32_t value = 10 + i;\n+\n+\t\t\tctrls.set(V4L2_CID_BRIGHTNESS, value);\n+\t\t\tctrls.set(V4L2_CID_CONTRAST, value);\n+\t\t\tdelayed->push(ctrls);\n+\t\t}\n+\n+\t\t/* Process all queued controls. */\n+\t\tfor (unsigned int i = 0; i < 16; i++) {\n+\t\t\tint32_t value = 10 + i;\n+\n+\t\t\tdelayed->applyControls(i);\n+\n+\t\t\tControlList result = delayed->get(i);\n+\n+\t\t\tint32_t brightness = result.get(V4L2_CID_BRIGHTNESS).get<int32_t>();\n+\t\t\tint32_t contrast = result.get(V4L2_CID_CONTRAST).get<int32_t>();\n+\t\t\tif (brightness != expected || contrast != expected) {\n+\t\t\t\tcerr << \"Failed multi queue\"\n+\t\t\t\t     << \" frame \" << i\n+\t\t\t\t     << \" brightness \" << brightness\n+\t\t\t\t     << \" contrast \" << contrast\n+\t\t\t\t     << \" expected \" << expected\n+\t\t\t\t     << endl;\n+\t\t\t\treturn TestFail;\n+\t\t\t}\n+\n+\t\t\texpected = i < 1 ? expected : value - 1;\n+\t\t}\n+\n+\t\treturn TestPass;\n+\t}\n+\n+\tint run() override\n+\t{\n+\t\tint ret;\n+\n+\t\t/* Test single control without delay. */\n+\t\tret = singleControlNoDelay();\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\t/* Test single control with delay. */\n+\t\tret = singleControlWithDelay();\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\t/* Test dual controls with different delays. */\n+\t\tret = dualControlsWithDelay(0);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\t/* Test dual controls with non-zero sequence start. */\n+\t\tret = dualControlsWithDelay(10000);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\t/* Test dual controls with sequence number wraparound. */\n+\t\tret = dualControlsWithDelay(UINT32_MAX - 50);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\t/* Test control values produced faster then consumed. */\n+\t\tret = dualControlsMultiQueue();\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\treturn TestPass;\n+\t}\n+\n+\tvoid cleanup() override\n+\t{\n+\t\tdelete dev_;\n+\t}\n+\n+private:\n+\tstd::unique_ptr<DeviceEnumerator> enumerator_;\n+\tstd::shared_ptr<MediaDevice> media_;\n+\tV4L2VideoDevice *dev_;\n+};\n+\n+TEST_REGISTER(DelayedControlsTest)\ndiff --git a/test/meson.build b/test/meson.build\nindex 0a1d434e399641bb..a683a657a439b4ff 100644\n--- a/test/meson.build\n+++ b/test/meson.build\n@@ -25,6 +25,7 @@ public_tests = [\n internal_tests = [\n     ['byte-stream-buffer',              'byte-stream-buffer.cpp'],\n     ['camera-sensor',                   'camera-sensor.cpp'],\n+    ['delayed_contols',                 'delayed_contols.cpp'],\n     ['event',                           'event.cpp'],\n     ['event-dispatcher',                'event-dispatcher.cpp'],\n     ['event-thread',                    'event-thread.cpp'],\n",
    "prefixes": [
        "libcamera-devel",
        "v4",
        "2/8"
    ]
}