/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2022, Google Inc. * * yaml-parser.cpp - YAML parser operations tests */ #include #include #include #include #include #include #include #include "libcamera/internal/yaml_parser.h" #include "test.h" using namespace libcamera; using namespace std; static const string testYaml = "string: libcamera\n" "double: 3.14159\n" "int8_t: -100\n" "uint8_t: 100\n" "int16_t: -1000\n" "uint16_t: 1000\n" "int32_t: -100000\n" "uint32_t: 100000\n" "size: [1920, 1080]\n" "list:\n" " - James\n" " - Mary\n" "dictionary:\n" " a: 1\n" " c: 3\n" " b: 2\n" "level1:\n" " level2:\n" " - [1, 2]\n" " - {one: 1, two: 2}\n"; static const string invalidYaml = "Invalid : - YAML : - Content"; class YamlParserTest : public Test { protected: bool createFile(const string &content, string &filename) { filename = "/tmp/libcamera.test.XXXXXX"; int fd = mkstemp(&filename.front()); if (fd == -1) return false; int ret = write(fd, content.c_str(), content.size()); close(fd); if (ret != static_cast(content.size())) return false; return true; } int init() { if (!createFile(testYaml, testYamlFile_)) return TestFail; if (!createFile(invalidYaml, invalidYamlFile_)) return TestFail; return TestPass; } enum class Type { String, Int8, UInt8, Int16, UInt16, Int32, UInt32, Double, Size, List, Dictionary, }; int testObjectType(const YamlObject &obj, const char *name, Type type) { bool isList = type == Type::List || type == Type::Size; bool isScalar = !isList && type != Type::Dictionary; bool isInteger8 = type == Type::Int8 || type == Type::UInt8; bool isInteger16 = type == Type::Int16 || type == Type::UInt16; bool isInteger32 = type == Type::Int32 || type == Type::UInt32; bool isIntegerUpTo16 = isInteger8 || isInteger16; bool isIntegerUpTo32 = isIntegerUpTo16 || isInteger32; bool isSigned = type == Type::Int8 || type == Type::Int16 || type == Type::Int32; if ((isScalar && !obj.isValue()) || (!isScalar && obj.isValue())) { std::cerr << "Object " << name << " type mismatch when compared to " << "value" << std::endl; return TestFail; } if ((isList && !obj.isList()) || (!isList && obj.isList())) { std::cerr << "Object " << name << " type mismatch when compared to " << "list" << std::endl; return TestFail; } if ((type == Type::Dictionary && !obj.isDictionary()) || (type != Type::Dictionary && obj.isDictionary())) { std::cerr << "Object " << name << " type mismatch when compared to " << "dictionary" << std::endl; return TestFail; } if (!isScalar && obj.get()) { std::cerr << "Object " << name << " didn't fail to parse as " << "string" << std::endl; return TestFail; } if (!isInteger8 && obj.get()) { std::cerr << "Object " << name << " didn't fail to parse as " << "int8_t" << std::endl; return TestFail; } if ((!isInteger8 || isSigned) && obj.get()) { std::cerr << "Object " << name << " didn't fail to parse as " << "uint8_t" << std::endl; return TestFail; } if (!isIntegerUpTo16 && obj.get()) { std::cerr << "Object " << name << " didn't fail to parse as " << "int16_t" << std::endl; return TestFail; } if ((!isIntegerUpTo16 || isSigned) && obj.get()) { std::cerr << "Object " << name << " didn't fail to parse as " << "uint16_t" << std::endl; return TestFail; } if (!isIntegerUpTo32 && obj.get()) { std::cerr << "Object " << name << " didn't fail to parse as " << "int32_t" << std::endl; return TestFail; } if ((!isIntegerUpTo32 || isSigned) && obj.get()) { std::cerr << "Object " << name << " didn't fail to parse as " << "uint32_t" << std::endl; return TestFail; } if (!isIntegerUpTo32 && type != Type::Double && obj.get()) { std::cerr << "Object " << name << " didn't fail to parse as " << "double" << std::endl; return TestFail; } if (type != Type::Size && obj.get()) { std::cerr << "Object " << name << " didn't fail to parse as " << "Size" << std::endl; return TestFail; } return TestPass; } int testIntegerObject(const YamlObject &obj, const char *name, Type type, int64_t value) { uint64_t unsignedValue = static_cast(value); std::string strValue = std::to_string(value); bool isInteger8 = type == Type::Int8 || type == Type::UInt8; bool isInteger16 = type == Type::Int16 || type == Type::UInt16; bool isSigned = type == Type::Int8 || type == Type::Int16 || type == Type::Int32; /* All integers can be parsed as strings or double. */ if (obj.get().value_or("") != strValue || obj.get("") != strValue) { std::cerr << "Object " << name << " failed to parse as " << "string" << std::endl; return TestFail; } if (obj.get().value_or(0.0) != value || obj.get(0.0) != value) { std::cerr << "Object " << name << " failed to parse as " << "double" << std::endl; return TestFail; } if (isInteger8) { if (obj.get().value_or(0) != value || obj.get(0) != value) { std::cerr << "Object " << name << " failed to parse as " << "int8_t" << std::endl; return TestFail; } } if (isInteger8 && !isSigned) { if (obj.get().value_or(0) != unsignedValue || obj.get(0) != unsignedValue) { std::cerr << "Object " << name << " failed to parse as " << "uint8_t" << std::endl; return TestFail; } } if (isInteger8 || isInteger16) { if (obj.get().value_or(0) != value || obj.get(0) != value) { std::cerr << "Object " << name << " failed to parse as " << "int16_t" << std::endl; return TestFail; } } if ((isInteger8 || isInteger16) && !isSigned) { if (obj.get().value_or(0) != unsignedValue || obj.get(0) != unsignedValue) { std::cerr << "Object " << name << " failed to parse as " << "uint16_t" << std::endl; return TestFail; } } if (obj.get().value_or(0) != value || obj.get(0) != value) { std::cerr << "Object " << name << " failed to parse as " << "int32_t" << std::endl; return TestFail; } if (!isSigned) { if (obj.get().value_or(0) != unsignedValue || obj.get(0) != unsignedValue) { std::cerr << "Object " << name << " failed to parse as " << "uint32_t" << std::endl; return TestFail; } } return TestPass; } int run() { /* Test invalid YAML file */ File file{ invalidYamlFile_ }; if (!file.open(File::OpenModeFlag::ReadOnly)) { cerr << "Fail to open invalid YAML file" << std::endl; return TestFail; } std::unique_ptr root = YamlParser::parse(file); if (root) { cerr << "Invalid YAML file parse successfully" << std::endl; return TestFail; } /* Test YAML file */ file.close(); file.setFileName(testYamlFile_); if (!file.open(File::OpenModeFlag::ReadOnly)) { cerr << "Fail to open test YAML file" << std::endl; return TestFail; } root = YamlParser::parse(file); if (!root) { cerr << "Fail to parse test YAML file: " << std::endl; return TestFail; } if (!root->isDictionary()) { cerr << "YAML root is not dictionary" << std::endl; return TestFail; } std::vector rootElemNames = { "string", "double", "int8_t", "uint8_t", "int16_t", "uint16_t", "int32_t", "uint32_t", "size", "list", "dictionary", "level1", }; for (const char *name : rootElemNames) { if (!root->contains(name)) { cerr << "Missing " << name << " object in YAML root" << std::endl; return TestFail; } } /* Test string object */ auto &strObj = (*root)["string"]; if (testObjectType(strObj, "string", Type::String) != TestPass) return TestFail; if (strObj.get().value_or("") != "libcamera" || strObj.get("") != "libcamera") { cerr << "String object parse as wrong content" << std::endl; return TestFail; } /* Test int8_t object */ auto &int8Obj = (*root)["int8_t"]; if (testObjectType(int8Obj, "int8_t", Type::Int8) != TestPass) return TestFail; if (testIntegerObject(int8Obj, "int8_t", Type::Int8, -100) != TestPass) return TestFail; /* Test uint8_t object */ auto &uint8Obj = (*root)["uint8_t"]; if (testObjectType(uint8Obj, "uint8_t", Type::UInt8) != TestPass) return TestFail; if (testIntegerObject(uint8Obj, "uint8_t", Type::UInt8, 100) != TestPass) return TestFail; /* Test int16_t object */ auto &int16Obj = (*root)["int16_t"]; if (testObjectType(int16Obj, "int16_t", Type::Int16) != TestPass) return TestFail; if (testIntegerObject(int16Obj, "int16_t", Type::Int16, -1000) != TestPass) return TestFail; /* Test uint16_t object */ auto &uint16Obj = (*root)["uint16_t"]; if (testObjectType(uint16Obj, "uint16_t", Type::UInt16) != TestPass) return TestFail; if (testIntegerObject(uint16Obj, "uint16_t", Type::UInt16, 1000) != TestPass) return TestFail; /* Test int32_t object */ auto &int32Obj = (*root)["int32_t"]; if (testObjectType(int32Obj, "int32_t", Type::Int32) != TestPass) return TestFail; if (testIntegerObject(int32Obj, "int32_t", Type::Int32, -100000) != TestPass) return TestFail; /* Test uint32_t object */ auto &uint32Obj = (*root)["uint32_t"]; if (testObjectType(uint32Obj, "uint32_t", Type::UInt32) != TestPass) return TestFail; if (testIntegerObject(uint32Obj, "uint32_t", Type::UInt32, 100000) != TestPass) return TestFail; /* Test double value */ auto &doubleObj = (*root)["double"]; if (testObjectType(doubleObj, "double", Type::Double) != TestPass) return TestFail; if (doubleObj.get().value_or("") != "3.14159" || doubleObj.get("") != "3.14159") { cerr << "Double object fail to parse as string" << std::endl; return TestFail; } if (doubleObj.get().value_or(0.0) != 3.14159 || doubleObj.get(0.0) != 3.14159) { cerr << "Double object parse as wrong value" << std::endl; return TestFail; } /* Test Size value */ auto &sizeObj = (*root)["size"]; if (testObjectType(sizeObj, "size", Type::Size) != TestPass) return TestFail; if (sizeObj.get().value_or(Size(0, 0)) != Size(1920, 1080) || sizeObj.get(Size(0, 0)) != Size(1920, 1080)) { cerr << "Size object parse as wrong value" << std::endl; return TestFail; } /* Test list object */ auto &listObj = (*root)["list"]; if (testObjectType(listObj, "list", Type::List) != TestPass) return TestFail; static constexpr std::array listValues{ "James", "Mary", }; if (listObj.size() != listValues.size()) { cerr << "List object parse with wrong size" << std::endl; return TestFail; } unsigned int i = 0; for (auto &elem : listObj.asList()) { if (i >= listValues.size()) { std::cerr << "Too many elements in list during iteration" << std::endl; return TestFail; } std::string value = listValues[i]; if (&elem != &listObj[i]) { std::cerr << "List element " << i << " has wrong address" << std::endl; return TestFail; } if (elem.get("") != value) { std::cerr << "List element " << i << " has wrong value" << std::endl; return TestFail; } i++; } /* Test dictionary object */ auto &dictObj = (*root)["dictionary"]; if (testObjectType(dictObj, "dictionary", Type::Dictionary) != TestPass) return TestFail; static constexpr std::array, 3> dictValues{ { { "a", 1 }, { "c", 3 }, { "b", 2 }, } }; size_t dictSize = dictValues.size(); if (dictObj.size() != dictSize) { cerr << "Dictionary object has wrong size" << std::endl; return TestFail; } i = 0; for (const auto &[key, elem] : dictObj.asDict()) { if (i >= dictSize) { std::cerr << "Too many elements in dictionary during iteration" << std::endl; return TestFail; } const auto &item = dictValues[i]; if (item.first != key) { std::cerr << "Dictionary key " << i << " has wrong value" << std::endl; return TestFail; } if (&elem != &dictObj[key]) { std::cerr << "Dictionary element " << i << " has wrong address" << std::endl; return TestFail; } if (elem.get(0) != item.second) { std::cerr << "Dictionary element " << i << " has wrong value" << std::endl; return TestFail; } i++; } /* Make sure utils::map_keys() works on the adapter. */ (void)utils::map_keys(dictObj.asDict()); /* Test leveled objects */ auto &level1Obj = (*root)["level1"]; if (!level1Obj.isDictionary()) { cerr << "level1 object fail to parse as Dictionary" << std::endl; return TestFail; } auto &level2Obj = level1Obj["level2"]; if (!level2Obj.isList() || level2Obj.size() != 2) { cerr << "level2 object should be 2 element list" << std::endl; return TestFail; } auto &firstElement = level2Obj[0]; if (!firstElement.isList() || firstElement.size() != 2 || firstElement[0].get(0) != 1 || firstElement[1].get(0) != 2) { cerr << "The first element of level2 object fail to parse as integer list" << std::endl; return TestFail; } const auto &values = firstElement.getList(); if (!values || values->size() != 2 || (*values)[0] != 1 || (*values)[1] != 2) { cerr << "getList() failed to return correct vector" << std::endl; return TestFail; } auto &secondElement = level2Obj[1]; if (!secondElement.isDictionary() || !secondElement.contains("one") || !secondElement.contains("two") || secondElement["one"].get(0) != 1 || secondElement["two"].get(0) != 2) { cerr << "The second element of level2 object fail to parse as dictionary" << std::endl; return TestFail; } return TestPass; } void cleanup() { unlink(testYamlFile_.c_str()); unlink(invalidYamlFile_.c_str()); } private: std::string testYamlFile_; std::string invalidYamlFile_; }; TEST_REGISTER(YamlParserTest) href='#n311'>311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
{
    "version": 2.0,
    "target": "bcm2835",
    "algorithms": [
        {
            "rpi.black_level":
            {
                "black_level": 1024
            }
        },
        {
            "rpi.dpc": { }
        },
        {
            "rpi.lux":
            {
                "reference_shutter_speed": 21663,
                "reference_gain": 1.0,
                "reference_aperture": 1.0,
                "reference_lux": 987,
                "reference_Y": 8961
            }
        },
        {
            "rpi.noise":
            {
                "reference_constant": 0,
                "reference_slope": 4.25
            }
        },
        {
            "rpi.geq":
            {
                "offset": 401,
                "slope": 0.05619
            }
        },
        {
            "rpi.sdn": { }
        },
        {
            "rpi.awb":
            {
                "bayes": 0
            }
        },
        {
            "rpi.agc":
            {
                "metering_modes":
                {
                    "centre-weighted":
                    {
                        "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ]
                    },
                    "spot":
                    {
                        "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
                    },
                    "matrix":
                    {
                        "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ]
                    }
                },
                "exposure_modes":
                {
                    "normal":
                    {
                        "shutter": [ 100, 10000, 30000, 60000, 66666 ],
                        "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ]
                    },
                    "short":
                    {
                        "shutter": [ 100, 5000, 10000, 20000, 33333 ],
                        "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ]
                    },
                    "long":
                    {
                        "shutter": [ 100, 10000, 30000, 60000, 120000 ],
                        "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ]
                    }
                },
                "constraint_modes":
                {
                    "normal": [
                        {
                            "bound": "LOWER",
                            "q_lo": 0.98,
                            "q_hi": 1.0,
                            "y_target":
                            [
                                0, 0.5,
                                1000, 0.5
                            ]
                        }
                    ],
                    "highlight": [
                        {
                            "bound": "LOWER",
                            "q_lo": 0.98,
                            "q_hi": 1.0,
                            "y_target":
                            [
                                0, 0.5,
                                1000, 0.5
                            ]
                        },
                        {
                            "bound": "UPPER",
                            "q_lo": 0.98,
                            "q_hi": 1.0,
                            "y_target":
                            [
                                0, 0.8,
                                1000, 0.8
                            ]
                        }
                    ],
                    "shadows": [
                        {
                            "bound": "LOWER",
                            "q_lo": 0.0,
                            "q_hi": 0.5,
                            "y_target":
                            [
                                0, 0.17,
                                1000, 0.17
                            ]
                        }
                    ]
                },
                "y_target":
                [
                    0, 0.16,
                    1000, 0.165,
                    10000, 0.17
                ],
                "base_ev": 1.25
            }
        },
        {
            "rpi.alsc":
            {
                "omega": 1.3,
                "n_iter": 100,
                "luminance_strength": 0.5,
                "calibrations_Cr": [
                    {
                        "ct": 3000,
                        "table":
                        [
                            1.105, 1.103, 1.093, 1.083, 1.071, 1.065, 1.065, 1.065, 1.066, 1.069, 1.072, 1.077, 1.084, 1.089, 1.093, 1.093,
                            1.103, 1.096, 1.084, 1.072, 1.059, 1.051, 1.047, 1.047, 1.051, 1.053, 1.059, 1.067, 1.075, 1.082, 1.085, 1.086,
                            1.096, 1.084, 1.072, 1.059, 1.051, 1.045, 1.039, 1.038, 1.039, 1.045, 1.049, 1.057, 1.063, 1.072, 1.081, 1.082,
                            1.092, 1.075, 1.061, 1.052, 1.045, 1.039, 1.036, 1.035, 1.035, 1.039, 1.044, 1.049, 1.056, 1.063, 1.072, 1.081,
                            1.092, 1.073, 1.058, 1.048, 1.043, 1.038, 1.035, 1.033, 1.033, 1.035, 1.039, 1.044, 1.051, 1.057, 1.069, 1.078,
                            1.091, 1.068, 1.054, 1.045, 1.041, 1.038, 1.035, 1.032, 1.032, 1.032, 1.036, 1.041, 1.045, 1.055, 1.069, 1.078,
                            1.091, 1.068, 1.052, 1.043, 1.041, 1.038, 1.035, 1.032, 1.031, 1.032, 1.034, 1.036, 1.043, 1.055, 1.069, 1.078,
                            1.092, 1.068, 1.052, 1.047, 1.042, 1.041, 1.038, 1.035, 1.032, 1.032, 1.035, 1.039, 1.043, 1.055, 1.071, 1.079,
                            1.092, 1.073, 1.057, 1.051, 1.047, 1.047, 1.044, 1.041, 1.038, 1.038, 1.039, 1.043, 1.051, 1.059, 1.076, 1.083,
                            1.092, 1.081, 1.068, 1.058, 1.056, 1.056, 1.053, 1.052, 1.049, 1.048, 1.048, 1.051, 1.059, 1.066, 1.083, 1.085,
                            1.091, 1.087, 1.081, 1.068, 1.065, 1.064, 1.062, 1.062, 1.061, 1.056, 1.056, 1.056, 1.064, 1.069, 1.084, 1.089,
                            1.091, 1.089, 1.085, 1.079, 1.069, 1.068, 1.067, 1.067, 1.067, 1.063, 1.061, 1.063, 1.068, 1.069, 1.081, 1.092
                        ]
                    },
                    {
                        "ct": 5000,
                        "table":
                        [
                            1.486, 1.484, 1.468, 1.449, 1.427, 1.403, 1.399, 1.399, 1.399, 1.404, 1.413, 1.433, 1.454, 1.473, 1.482, 1.488,
                            1.484, 1.472, 1.454, 1.431, 1.405, 1.381, 1.365, 1.365, 1.367, 1.373, 1.392, 1.411, 1.438, 1.458, 1.476, 1.481,
                            1.476, 1.458, 1.433, 1.405, 1.381, 1.361, 1.339, 1.334, 1.334, 1.346, 1.362, 1.391, 1.411, 1.438, 1.462, 1.474,
                            1.471, 1.443, 1.417, 1.388, 1.361, 1.339, 1.321, 1.313, 1.313, 1.327, 1.346, 1.362, 1.391, 1.422, 1.453, 1.473,
                            1.469, 1.439, 1.408, 1.377, 1.349, 1.321, 1.312, 1.299, 1.299, 1.311, 1.327, 1.348, 1.378, 1.415, 1.446, 1.468,
                            1.468, 1.434, 1.402, 1.371, 1.341, 1.316, 1.299, 1.296, 1.295, 1.299, 1.314, 1.338, 1.371, 1.408, 1.441, 1.466,
                            1.468, 1.434, 1.401, 1.371, 1.341, 1.316, 1.301, 1.296, 1.295, 1.297, 1.314, 1.338, 1.369, 1.408, 1.441, 1.465,
                            1.469, 1.436, 1.401, 1.374, 1.348, 1.332, 1.315, 1.301, 1.301, 1.313, 1.324, 1.342, 1.372, 1.409, 1.442, 1.465,
                            1.471, 1.444, 1.413, 1.388, 1.371, 1.348, 1.332, 1.323, 1.323, 1.324, 1.342, 1.362, 1.386, 1.418, 1.449, 1.467,
                            1.473, 1.454, 1.431, 1.407, 1.388, 1.371, 1.359, 1.352, 1.351, 1.351, 1.362, 1.383, 1.404, 1.433, 1.462, 1.472,
                            1.474, 1.461, 1.447, 1.424, 1.407, 1.394, 1.385, 1.381, 1.379, 1.381, 1.383, 1.401, 1.419, 1.444, 1.466, 1.481,
                            1.474, 1.464, 1.455, 1.442, 1.421, 1.408, 1.403, 1.403, 1.403, 1.399, 1.402, 1.415, 1.432, 1.446, 1.467, 1.483
                        ]
                    },
                    {
                        "ct": 6500,
                        "table":
                        [
                            1.567, 1.565, 1.555, 1.541, 1.525, 1.518, 1.518, 1.518, 1.521, 1.527, 1.532, 1.541, 1.551, 1.559, 1.567, 1.569,
                            1.565, 1.557, 1.542, 1.527, 1.519, 1.515, 1.511, 1.516, 1.519, 1.524, 1.528, 1.533, 1.542, 1.553, 1.559, 1.562,
                            1.561, 1.546, 1.532, 1.521, 1.518, 1.515, 1.511, 1.516, 1.519, 1.524, 1.528, 1.529, 1.533, 1.542, 1.554, 1.559,
                            1.561, 1.539, 1.526, 1.524, 1.521, 1.521, 1.522, 1.524, 1.525, 1.531, 1.529, 1.529, 1.531, 1.538, 1.549, 1.558,
                            1.559, 1.538, 1.526, 1.525, 1.524, 1.528, 1.534, 1.536, 1.536, 1.536, 1.532, 1.529, 1.531, 1.537, 1.548, 1.556,
                            1.561, 1.537, 1.525, 1.524, 1.526, 1.532, 1.537, 1.539, 1.538, 1.537, 1.532, 1.529, 1.529, 1.537, 1.546, 1.556,
                            1.561, 1.536, 1.524, 1.522, 1.525, 1.532, 1.538, 1.538, 1.537, 1.533, 1.528, 1.526, 1.527, 1.536, 1.546, 1.555,