{"id":26468,"url":"https://patchwork.libcamera.org/api/1.1/patches/26468/?format=json","web_url":"https://patchwork.libcamera.org/patch/26468/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20260407153427.1825999-29-laurent.pinchart@ideasonboard.com>","date":"2026-04-07T15:34:13","name":"[v2,28/42] test: yaml-parser: Simplify test","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"8189071ee4d5fbe965ecd197671b86447795f2ab","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/1.1/people/2/?format=json","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/26468/mbox/","series":[{"id":5873,"url":"https://patchwork.libcamera.org/api/1.1/series/5873/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=5873","date":"2026-04-07T15:33:45","name":"libcamera: Global configuration file improvements","version":2,"mbox":"https://patchwork.libcamera.org/series/5873/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/26468/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/26468/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 B02DBC3318\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue,  7 Apr 2026 15:35:12 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C204262E14;\n\tTue,  7 Apr 2026 17:35:11 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 1A3A062DF3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  7 Apr 2026 17:35:07 +0200 (CEST)","from killaraus.ideasonboard.com\n\t(2001-14ba-703d-e500--2a1.rev.dnainternet.fi\n\t[IPv6:2001:14ba:703d:e500::2a1])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 6709178E\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  7 Apr 2026 17:33:39 +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=\"u7Y+BOH4\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1775576019;\n\tbh=DAIODyJlod0XmZfvzA3o03Ugcr9m2dtZ71EOuQ5PcgQ=;\n\th=From:To:Subject:Date:In-Reply-To:References:From;\n\tb=u7Y+BOH45NfBT7KGLZ12A9M7dU6eCqq2Z92HD+0XScKwo4hruTBlBVOFVIGC9S0/L\n\tIWLENa/EVYbipdM8gcDCwLmyn8p758iVPAOYcB7kLowcH/Rh1lzM5H5L7DlQ38VfWd\n\tiHsyYEf/TZc2+oYpEHKnpcplYctrwVfbB9H0v0lk=","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"libcamera-devel@lists.libcamera.org","Subject":"[PATCH v2 28/42] test: yaml-parser: Simplify test","Date":"Tue,  7 Apr 2026 18:34:13 +0300","Message-ID":"<20260407153427.1825999-29-laurent.pinchart@ideasonboard.com>","X-Mailer":"git-send-email 2.52.0","In-Reply-To":"<20260407153427.1825999-1-laurent.pinchart@ideasonboard.com>","References":"<20260407153427.1825999-1-laurent.pinchart@ideasonboard.com>","MIME-Version":"1.0","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":"Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n---\n test/yaml-parser.cpp | 469 +++++--------------------------------------\n 1 file changed, 50 insertions(+), 419 deletions(-)","diff":"diff --git a/test/yaml-parser.cpp b/test/yaml-parser.cpp\nindex 8c5826f4885b..0bbda3af8ea9 100644\n--- a/test/yaml-parser.cpp\n+++ b/test/yaml-parser.cpp\n@@ -12,7 +12,6 @@\n #include <unistd.h>\n \n #include <libcamera/base/file.h>\n-#include <libcamera/base/utils.h>\n \n #include <libcamera/geometry.h>\n \n@@ -24,24 +23,12 @@ using namespace libcamera;\n using namespace std;\n \n static const string testYaml =\n-\t\"string: libcamera\\n\"\n-\t\"double: 3.14159\\n\"\n-\t\"int8_t: -100\\n\"\n-\t\"uint8_t: 100\\n\"\n-\t\"int16_t: -1000\\n\"\n-\t\"uint16_t: 1000\\n\"\n-\t\"int32_t: -100000\\n\"\n-\t\"uint32_t: 100000\\n\"\n-\t\"size: [1920, 1080]\\n\"\n+\t\"empty:\\n\"\n+\t\"value: 42\\n\"\n \t\"list:\\n\"\n-\t\"  - James\\n\"\n-\t\"  - Mary\\n\"\n+\t\"  - libcamera\\n\"\n+\t\"  - linux\\n\"\n \t\"  - \\n\"\n-\t\"dictionary:\\n\"\n-\t\"  a: 1\\n\"\n-\t\"  c: 3\\n\"\n-\t\"  b: 2\\n\"\n-\t\"  empty:\\n\"\n \t\"level1:\\n\"\n \t\"  level2:\\n\"\n \t\"    - [1, 2]\\n\"\n@@ -80,212 +67,9 @@ protected:\n \t\treturn TestPass;\n \t}\n \n-\tenum class Type {\n-\t\tString,\n-\t\tInt8,\n-\t\tUInt8,\n-\t\tInt16,\n-\t\tUInt16,\n-\t\tInt32,\n-\t\tUInt32,\n-\t\tDouble,\n-\t\tSize,\n-\t\tList,\n-\t\tDictionary,\n-\t};\n-\n-\tint testObjectType(const ValueNode &obj, const char *name, Type type)\n-\t{\n-\t\tbool isList = type == Type::List || type == Type::Size;\n-\t\tbool isScalar = !isList && type != Type::Dictionary;\n-\t\tbool isInteger8 = type == Type::Int8 || type == Type::UInt8;\n-\t\tbool isInteger16 = type == Type::Int16 || type == Type::UInt16;\n-\t\tbool isInteger32 = type == Type::Int32 || type == Type::UInt32;\n-\t\tbool isIntegerUpTo16 = isInteger8 || isInteger16;\n-\t\tbool isIntegerUpTo32 = isIntegerUpTo16 || isInteger32;\n-\t\tbool isSigned = type == Type::Int8 || type == Type::Int16 ||\n-\t\t\t        type == Type::Int32;\n-\n-\t\tif ((isScalar && !obj.isValue()) || (!isScalar && obj.isValue())) {\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Object \" << name << \" type mismatch when compared to \"\n-\t\t\t\t<< \"value\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\tif ((isList && !obj.isList()) || (!isList && obj.isList())) {\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Object \" << name << \" type mismatch when compared to \"\n-\t\t\t\t<< \"list\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\tif ((type == Type::Dictionary && !obj.isDictionary()) ||\n-\t\t    (type != Type::Dictionary && obj.isDictionary())) {\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Object \" << name << \" type mismatch when compared to \"\n-\t\t\t\t<< \"dictionary\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\tif (!isScalar && obj.get<std::string>()) {\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Object \" << name << \" didn't fail to parse as \"\n-\t\t\t\t<< \"string\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\tif (!isInteger8 && obj.get<int8_t>()) {\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Object \" << name << \" didn't fail to parse as \"\n-\t\t\t\t<< \"int8_t\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\tif ((!isInteger8 || isSigned) && obj.get<uint8_t>()) {\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Object \" << name << \" didn't fail to parse as \"\n-\t\t\t\t<< \"uint8_t\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\tif (!isIntegerUpTo16 && obj.get<int16_t>()) {\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Object \" << name << \" didn't fail to parse as \"\n-\t\t\t\t<< \"int16_t\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\tif ((!isIntegerUpTo16 || isSigned) && obj.get<uint16_t>()) {\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Object \" << name << \" didn't fail to parse as \"\n-\t\t\t\t<< \"uint16_t\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\tif (!isIntegerUpTo32 && obj.get<int32_t>()) {\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Object \" << name << \" didn't fail to parse as \"\n-\t\t\t\t<< \"int32_t\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\tif ((!isIntegerUpTo32 || isSigned) && obj.get<uint32_t>()) {\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Object \" << name << \" didn't fail to parse as \"\n-\t\t\t\t<< \"uint32_t\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\tif (!isIntegerUpTo32 && type != Type::Double && obj.get<double>()) {\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Object \" << name << \" didn't fail to parse as \"\n-\t\t\t\t<< \"double\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\tif (type != Type::Size && obj.get<Size>()) {\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Object \" << name << \" didn't fail to parse as \"\n-\t\t\t\t<< \"Size\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\treturn TestPass;\n-\t}\n-\n-\tint testIntegerObject(const ValueNode &obj, const char *name, Type type,\n-\t\t\t      int64_t value)\n-\t{\n-\t\tuint64_t unsignedValue = static_cast<uint64_t>(value);\n-\t\tstd::string strValue = std::to_string(value);\n-\t\tbool isInteger8 = type == Type::Int8 || type == Type::UInt8;\n-\t\tbool isInteger16 = type == Type::Int16 || type == Type::UInt16;\n-\t\tbool isSigned = type == Type::Int8 || type == Type::Int16 ||\n-\t\t\t\ttype == Type::Int32;\n-\n-\t\t/* All integers can be parsed as strings or double. */\n-\n-\t\tif (obj.get<string>().value_or(\"\") != strValue ||\n-\t\t    obj.get<string>(\"\") != strValue) {\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Object \" << name << \" failed to parse as \"\n-\t\t\t\t<< \"string\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\tif (obj.get<double>().value_or(0.0) != value ||\n-\t\t    obj.get<double>(0.0) != value) {\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Object \" << name << \" failed to parse as \"\n-\t\t\t\t<< \"double\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\tif (isInteger8) {\n-\t\t\tif (obj.get<int8_t>().value_or(0) != value ||\n-\t\t\t    obj.get<int8_t>(0) != value) {\n-\t\t\t\tstd::cerr\n-\t\t\t\t\t<< \"Object \" << name << \" failed to parse as \"\n-\t\t\t\t\t<< \"int8_t\" << std::endl;\n-\t\t\t\treturn TestFail;\n-\t\t\t}\n-\t\t}\n-\n-\t\tif (isInteger8 && !isSigned) {\n-\t\t\tif (obj.get<uint8_t>().value_or(0) != unsignedValue ||\n-\t\t\t    obj.get<uint8_t>(0) != unsignedValue) {\n-\t\t\t\tstd::cerr\n-\t\t\t\t\t<< \"Object \" << name << \" failed to parse as \"\n-\t\t\t\t\t<< \"uint8_t\" << std::endl;\n-\t\t\t\treturn TestFail;\n-\t\t\t}\n-\t\t}\n-\n-\t\tif (isInteger8 || isInteger16) {\n-\t\t\tif (obj.get<int16_t>().value_or(0) != value ||\n-\t\t\t    obj.get<int16_t>(0) != value) {\n-\t\t\t\tstd::cerr\n-\t\t\t\t\t<< \"Object \" << name << \" failed to parse as \"\n-\t\t\t\t\t<< \"int16_t\" << std::endl;\n-\t\t\t\treturn TestFail;\n-\t\t\t}\n-\t\t}\n-\n-\t\tif ((isInteger8 || isInteger16) && !isSigned) {\n-\t\t\tif (obj.get<uint16_t>().value_or(0) != unsignedValue ||\n-\t\t\t    obj.get<uint16_t>(0) != unsignedValue) {\n-\t\t\t\tstd::cerr\n-\t\t\t\t\t<< \"Object \" << name << \" failed to parse as \"\n-\t\t\t\t\t<< \"uint16_t\" << std::endl;\n-\t\t\t\treturn TestFail;\n-\t\t\t}\n-\t\t}\n-\n-\t\tif (obj.get<int32_t>().value_or(0) != value ||\n-\t\t    obj.get<int32_t>(0) != value) {\n-\t\t\tstd::cerr\n-\t\t\t\t<< \"Object \" << name << \" failed to parse as \"\n-\t\t\t\t<< \"int32_t\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\tif (!isSigned) {\n-\t\t\tif (obj.get<uint32_t>().value_or(0) != unsignedValue ||\n-\t\t\t    obj.get<uint32_t>(0) != unsignedValue) {\n-\t\t\t\tstd::cerr\n-\t\t\t\t\t<< \"Object \" << name << \" failed to parse as \"\n-\t\t\t\t\t<< \"uint32_t\" << std::endl;\n-\t\t\t\treturn TestFail;\n-\t\t\t}\n-\t\t}\n-\n-\t\treturn TestPass;\n-\t}\n-\n \tint run()\n \t{\n-\t\t/* Test invalid YAML file */\n+\t\t/* Test parsing invalid YAML file. */\n \t\tFile file{ invalidYamlFile_ };\n \t\tif (!file.open(File::OpenModeFlag::ReadOnly)) {\n \t\t\tcerr << \"Fail to open invalid YAML file\" << std::endl;\n@@ -298,7 +82,7 @@ protected:\n \t\t\treturn TestFail;\n \t\t}\n \n-\t\t/* Test YAML file */\n+\t\t/* Test parsing valid YAML file. */\n \t\tfile.close();\n \t\tfile.setFileName(testYamlFile_);\n \t\tif (!file.open(File::OpenModeFlag::ReadOnly)) {\n@@ -313,130 +97,66 @@ protected:\n \t\t\treturn TestFail;\n \t\t}\n \n+\t\t/* Test that the root dictionary node has been parsed correctly. */\n \t\tif (!root->isDictionary()) {\n-\t\t\tcerr << \"YAML root is not dictionary\" << std::endl;\n+\t\t\tcerr << \"Dictionary node has wrong type\" << std::endl;\n \t\t\treturn TestFail;\n \t\t}\n \n-\t\tstd::vector<const char *> rootElemNames = {\n-\t\t\t\"string\", \"double\", \"int8_t\", \"uint8_t\", \"int16_t\",\n-\t\t\t\"uint16_t\", \"int32_t\", \"uint32_t\", \"size\", \"list\",\n-\t\t\t\"dictionary\", \"level1\",\n-\t\t};\n+\t\tusing NodeFunc = bool (ValueNode::*)() const;\n \n-\t\tfor (const char *name : rootElemNames) {\n-\t\t\tif (!root->contains(name)) {\n-\t\t\t\tcerr << \"Missing \" << name << \" object in YAML root\"\n-\t\t\t\t     << std::endl;\n+\t\tstd::map<std::string, NodeFunc> topLevelNodes = { {\n+\t\t\t{ \"empty\", &ValueNode::isValue },\n+\t\t\t{ \"value\", &ValueNode::isValue },\n+\t\t\t{ \"list\", &ValueNode::isList },\n+\t\t\t{ \"level1\", &ValueNode::isDictionary },\n+\t\t} };\n+\n+\t\tif (root->size() != topLevelNodes.size()) {\n+\t\t\tstd::cerr << \"Dictionary node has wrong size\" << std::endl;\n+\t\t\treturn TestFail;\n+\t\t}\n+\n+\t\tfor (const auto &[key, value] : root->asDict()) {\n+\t\t\tconst auto iter = topLevelNodes.find(key);\n+\t\t\tif (iter == topLevelNodes.end()) {\n+\t\t\t\tstd::cerr << \"Dictionary key '\" << key << \"' unknown\"\n+\t\t\t\t\t  << std::endl;\n \t\t\t\treturn TestFail;\n \t\t\t}\n+\n+\t\t\tconst auto &func = iter->second;\n+\t\t\tif (!(value.*func)()) {\n+\t\t\t\tstd::cerr << \"Node '\" << key << \"' has wrong type\"\n+\t\t\t\t\t  << std::endl;\n+\t\t\t\treturn TestFail;\n+\t\t\t}\n+\n+\t\t\ttopLevelNodes.erase(iter);\n \t\t}\n \n-\t\t/* Test string object */\n-\t\tauto &strObj = (*root)[\"string\"];\n+\t\t/* Test empty node. */\n+\t\tauto &emptyNode = (*root)[\"empty\"];\n \n-\t\tif (testObjectType(strObj, \"string\", Type::String) != TestPass)\n-\t\t\treturn TestFail;\n-\n-\t\tif (strObj.get<string>().value_or(\"\") != \"libcamera\" ||\n-\t\t    strObj.get<string>(\"\") != \"libcamera\") {\n-\t\t\tcerr << \"String object parse as wrong content\" << std::endl;\n+\t\tif (emptyNode.get<string>(\"-\") != \"\") {\n+\t\t\tstd::cerr << \"Empty node has incorrect content\" << std::endl;\n \t\t\treturn TestFail;\n \t\t}\n \n-\t\t/* Test int8_t object */\n-\t\tauto &int8Obj = (*root)[\"int8_t\"];\n+\t\t/* Test value node. */\n+\t\tauto &valueNode = (*root)[\"value\"];\n \n-\t\tif (testObjectType(int8Obj, \"int8_t\", Type::Int8) != TestPass)\n-\t\t\treturn TestFail;\n-\n-\t\tif (testIntegerObject(int8Obj, \"int8_t\", Type::Int8, -100) != TestPass)\n-\t\t\treturn TestFail;\n-\n-\t\t/* Test uint8_t object */\n-\t\tauto &uint8Obj = (*root)[\"uint8_t\"];\n-\n-\t\tif (testObjectType(uint8Obj, \"uint8_t\", Type::UInt8) != TestPass)\n-\t\t\treturn TestFail;\n-\n-\t\tif (testIntegerObject(uint8Obj, \"uint8_t\", Type::UInt8, 100) != TestPass)\n-\t\t\treturn TestFail;\n-\n-\t\t/* Test int16_t object */\n-\t\tauto &int16Obj = (*root)[\"int16_t\"];\n-\n-\t\tif (testObjectType(int16Obj, \"int16_t\", Type::Int16) != TestPass)\n-\t\t\treturn TestFail;\n-\n-\t\tif (testIntegerObject(int16Obj, \"int16_t\", Type::Int16, -1000) != TestPass)\n-\t\t\treturn TestFail;\n-\n-\t\t/* Test uint16_t object */\n-\t\tauto &uint16Obj = (*root)[\"uint16_t\"];\n-\n-\t\tif (testObjectType(uint16Obj, \"uint16_t\", Type::UInt16) != TestPass)\n-\t\t\treturn TestFail;\n-\n-\t\tif (testIntegerObject(uint16Obj, \"uint16_t\", Type::UInt16, 1000) != TestPass)\n-\t\t\treturn TestFail;\n-\n-\t\t/* Test int32_t object */\n-\t\tauto &int32Obj = (*root)[\"int32_t\"];\n-\n-\t\tif (testObjectType(int32Obj, \"int32_t\", Type::Int32) != TestPass)\n-\t\t\treturn TestFail;\n-\n-\t\tif (testIntegerObject(int32Obj, \"int32_t\", Type::Int32, -100000) != TestPass)\n-\t\t\treturn TestFail;\n-\n-\t\t/* Test uint32_t object */\n-\t\tauto &uint32Obj = (*root)[\"uint32_t\"];\n-\n-\t\tif (testObjectType(uint32Obj, \"uint32_t\", Type::UInt32) != TestPass)\n-\t\t\treturn TestFail;\n-\n-\t\tif (testIntegerObject(uint32Obj, \"uint32_t\", Type::UInt32, 100000) != TestPass)\n-\t\t\treturn TestFail;\n-\n-\t\t/* Test double value */\n-\t\tauto &doubleObj = (*root)[\"double\"];\n-\n-\t\tif (testObjectType(doubleObj, \"double\", Type::Double) != TestPass)\n-\t\t\treturn TestFail;\n-\n-\t\tif (doubleObj.get<string>().value_or(\"\") != \"3.14159\" ||\n-\t\t    doubleObj.get<string>(\"\") != \"3.14159\") {\n-\t\t\tcerr << \"Double object fail to parse as string\" << std::endl;\n+\t\tif (valueNode.get<string>(\"\") != \"42\") {\n+\t\t\tstd::cerr << \"Value node has incorrect content\" << std::endl;\n \t\t\treturn TestFail;\n \t\t}\n \n-\t\tif (doubleObj.get<double>().value_or(0.0) != 3.14159 ||\n-\t\t    doubleObj.get<double>(0.0) != 3.14159) {\n-\t\t\tcerr << \"Double object parse as wrong value\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\t/* Test Size value */\n-\t\tauto &sizeObj = (*root)[\"size\"];\n-\n-\t\tif (testObjectType(sizeObj, \"size\", Type::Size) != TestPass)\n-\t\t\treturn TestFail;\n-\n-\t\tif (sizeObj.get<Size>().value_or(Size(0, 0)) != Size(1920, 1080) ||\n-\t\t    sizeObj.get<Size>(Size(0, 0)) != Size(1920, 1080)) {\n-\t\t\tcerr << \"Size object parse as wrong value\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\t/* Test list object */\n+\t\t/* Test list node. */\n \t\tauto &listObj = (*root)[\"list\"];\n \n-\t\tif (testObjectType(listObj, \"list\", Type::List) != TestPass)\n-\t\t\treturn TestFail;\n-\n \t\tstatic constexpr std::array<const char *, 3> listValues{\n-\t\t\t\"James\",\n-\t\t\t\"Mary\",\n+\t\t\t\"libcamera\",\n+\t\t\t\"linux\",\n \t\t\t\"\",\n \t\t};\n \n@@ -470,102 +190,13 @@ protected:\n \t\t\ti++;\n \t\t}\n \n-\t\t/* Ensure that empty objects get parsed as empty strings. */\n+\t\t/* Ensure that empty list elements get parsed as empty strings. */\n \t\tif (!listObj[2].isValue()) {\n-\t\t\tcerr << \"Empty object is not a value\" << std::endl;\n+\t\t\tcerr << \"Empty list element is not a value\" << std::endl;\n \t\t\treturn TestFail;\n \t\t}\n \n-\t\t/* Test dictionary object */\n-\t\tauto &dictObj = (*root)[\"dictionary\"];\n-\n-\t\tif (testObjectType(dictObj, \"dictionary\", Type::Dictionary) != TestPass)\n-\t\t\treturn TestFail;\n-\n-\t\tstatic constexpr std::array<std::pair<const char *, int>, 4> dictValues{ {\n-\t\t\t{ \"a\", 1 },\n-\t\t\t{ \"c\", 3 },\n-\t\t\t{ \"b\", 2 },\n-\t\t\t{ \"empty\", -100 },\n-\t\t} };\n-\n-\t\tsize_t dictSize = dictValues.size();\n-\n-\t\tif (dictObj.size() != dictSize) {\n-\t\t\tcerr << \"Dictionary object has wrong size\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\ti = 0;\n-\t\tfor (const auto &[key, elem] : dictObj.asDict()) {\n-\t\t\tif (i >= dictSize) {\n-\t\t\t\tstd::cerr << \"Too many elements in dictionary during iteration\"\n-\t\t\t\t\t  << std::endl;\n-\t\t\t\treturn TestFail;\n-\t\t\t}\n-\n-\t\t\tconst auto &item = dictValues[i];\n-\t\t\tif (item.first != key) {\n-\t\t\t\tstd::cerr << \"Dictionary key \" << i << \" has wrong value\"\n-\t\t\t\t\t  << std::endl;\n-\t\t\t\treturn TestFail;\n-\t\t\t}\n-\n-\t\t\tif (&elem != &dictObj[key]) {\n-\t\t\t\tstd::cerr << \"Dictionary element \" << i << \" has wrong address\"\n-\t\t\t\t\t  << std::endl;\n-\t\t\t\treturn TestFail;\n-\t\t\t}\n-\n-\t\t\tif (elem.get<int32_t>(-100) != item.second) {\n-\t\t\t\tstd::cerr << \"Dictionary element \" << i << \" has wrong value\"\n-\t\t\t\t\t  << std::endl;\n-\t\t\t\treturn TestFail;\n-\t\t\t}\n-\n-\t\t\ti++;\n-\t\t}\n-\n-\t\t/* Ensure that empty objects get parsed as empty strings. */\n-\t\tif (!dictObj[\"empty\"].isValue()) {\n-\t\t\tcerr << \"Empty object is not of type value\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\t/* Ensure that keys without values are added to a dict. */\n-\t\tif (!dictObj.contains(\"empty\")) {\n-\t\t\tcerr << \"Empty element is missing in dict\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\t/* Test access to nonexistent member. */\n-\t\tif (dictObj[\"nonexistent\"].get<std::string>(\"default\") != \"default\") {\n-\t\t\tcerr << \"Accessing nonexistent dict entry fails to return default\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\t/* Test nonexistent object has value type empty. */\n-\t\tif (!dictObj[\"nonexistent\"].isEmpty()) {\n-\t\t\tcerr << \"Accessing nonexistent object returns non-empty object\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\t/* Test explicit cast to bool on an empty object returns true. */\n-\t\tif (!!dictObj[\"empty\"] != true) {\n-\t\t\tcerr << \"Casting empty entry to bool returns false\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\t/* Test explicit cast to bool on nonexistent object returns false. */\n-\t\tif (!!dictObj[\"nonexistent\"] != false) {\n-\t\t\tcerr << \"Casting nonexistent dict entry to bool returns true\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\t/* Make sure utils::map_keys() works on the adapter. */\n-\t\t(void)utils::map_keys(dictObj.asDict());\n-\n-\t\t/* Test leveled objects */\n+\t\t/* Test nested nodes. */\n \t\tauto &level1Obj = (*root)[\"level1\"];\n \n \t\tif (!level1Obj.isDictionary()) {\n@@ -576,7 +207,7 @@ protected:\n \t\tauto &level2Obj = level1Obj[\"level2\"];\n \n \t\tif (!level2Obj.isList() || level2Obj.size() != 2) {\n-\t\t\tcerr << \"level2 object should be 2 element list\" << std::endl;\n+\t\t\tcerr << \"level2 object should be a 2 elements list\" << std::endl;\n \t\t\treturn TestFail;\n \t\t}\n \n","prefixes":["v2","28/42"]}