#include #include #include #include #include #include namespace { template bool checkNumber(sol::state_view& lua, const std::string& inputData, T requiredValue) { sol::object result = LuaUtil::loadYaml(inputData, lua); if (result.get_type() != sol::type::number) return false; return result.as() == requiredValue; } bool checkBool(sol::state_view& lua, const std::string& inputData, bool requiredValue) { sol::object result = LuaUtil::loadYaml(inputData, lua); if (result.get_type() != sol::type::boolean) return false; return result.as() == requiredValue; } bool checkNil(sol::state_view& lua, const std::string& inputData) { sol::object result = LuaUtil::loadYaml(inputData, lua); return result == sol::nil; } bool checkNan(sol::state_view& lua, const std::string& inputData) { sol::object result = LuaUtil::loadYaml(inputData, lua); if (result.get_type() != sol::type::number) return false; return std::isnan(result.as()); } bool checkString(sol::state_view& lua, const std::string& inputData, const std::string& requiredValue) { sol::object result = LuaUtil::loadYaml(inputData, lua); if (result.get_type() != sol::type::string) return false; return result.as() == requiredValue; } bool checkString(sol::state_view& lua, const std::string& inputData) { sol::object result = LuaUtil::loadYaml(inputData, lua); if (result.get_type() != sol::type::string) return false; return result.as() == inputData; } TEST(LuaUtilYamlLoader, ScalarTypeDeduction) { sol::state lua; ASSERT_TRUE(checkNil(lua, "null")); ASSERT_TRUE(checkNil(lua, "Null")); ASSERT_TRUE(checkNil(lua, "NULL")); ASSERT_TRUE(checkNil(lua, "~")); ASSERT_TRUE(checkNil(lua, "")); ASSERT_FALSE(checkNil(lua, "NUll")); ASSERT_TRUE(checkString(lua, "NUll")); ASSERT_TRUE(checkString(lua, "'null'", "null")); ASSERT_TRUE(checkNumber(lua, "017", 17)); ASSERT_TRUE(checkNumber(lua, "-017", -17)); ASSERT_TRUE(checkNumber(lua, "+017", 17)); ASSERT_TRUE(checkNumber(lua, "17", 17)); ASSERT_TRUE(checkNumber(lua, "-17", -17)); ASSERT_TRUE(checkNumber(lua, "+17", 17)); ASSERT_TRUE(checkNumber(lua, "0o17", 15)); ASSERT_TRUE(checkString(lua, "-0o17")); ASSERT_TRUE(checkString(lua, "+0o17")); ASSERT_TRUE(checkString(lua, "0b1")); ASSERT_TRUE(checkString(lua, "1:00")); ASSERT_TRUE(checkString(lua, "'17'", "17")); ASSERT_TRUE(checkNumber(lua, "0x17", 23)); ASSERT_TRUE(checkString(lua, "'-0x17'", "-0x17")); ASSERT_TRUE(checkString(lua, "'+0x17'", "+0x17")); ASSERT_TRUE(checkNumber(lua, "2.1e-05", 2.1e-5)); ASSERT_TRUE(checkNumber(lua, "-2.1e-05", -2.1e-5)); ASSERT_TRUE(checkNumber(lua, "+2.1e-05", 2.1e-5)); ASSERT_TRUE(checkNumber(lua, "2.1e+5", 210000)); ASSERT_TRUE(checkNumber(lua, "-2.1e+5", -210000)); ASSERT_TRUE(checkNumber(lua, "+2.1e+5", 210000)); ASSERT_TRUE(checkNumber(lua, "0.27", 0.27)); ASSERT_TRUE(checkNumber(lua, "-0.27", -0.27)); ASSERT_TRUE(checkNumber(lua, "+0.27", 0.27)); ASSERT_TRUE(checkNumber(lua, "2.7", 2.7)); ASSERT_TRUE(checkNumber(lua, "-2.7", -2.7)); ASSERT_TRUE(checkNumber(lua, "+2.7", 2.7)); ASSERT_TRUE(checkNumber(lua, ".27", 0.27)); ASSERT_TRUE(checkNumber(lua, "-.27", -0.27)); ASSERT_TRUE(checkNumber(lua, "+.27", 0.27)); ASSERT_TRUE(checkNumber(lua, "27.", 27.0)); ASSERT_TRUE(checkNumber(lua, "-27.", -27.0)); ASSERT_TRUE(checkNumber(lua, "+27.", 27.0)); ASSERT_TRUE(checkNan(lua, ".nan")); ASSERT_TRUE(checkNan(lua, ".NaN")); ASSERT_TRUE(checkNan(lua, ".NAN")); ASSERT_FALSE(checkNan(lua, "nan")); ASSERT_FALSE(checkNan(lua, ".nAn")); ASSERT_TRUE(checkString(lua, "'.nan'", ".nan")); ASSERT_TRUE(checkString(lua, ".nAn")); ASSERT_TRUE(checkNumber(lua, "1.7976931348623157E+308", std::numeric_limits::max())); ASSERT_TRUE(checkNumber(lua, "-1.7976931348623157E+308", std::numeric_limits::lowest())); ASSERT_TRUE(checkNumber(lua, "2.2250738585072014e-308", std::numeric_limits::min())); ASSERT_TRUE(checkNumber(lua, ".inf", std::numeric_limits::infinity())); ASSERT_TRUE(checkNumber(lua, "+.inf", std::numeric_limits::infinity())); ASSERT_TRUE(checkNumber(lua, "-.inf", -std::numeric_limits::infinity())); ASSERT_TRUE(checkNumber(lua, ".Inf", std::numeric_limits::infinity())); ASSERT_TRUE(checkNumber(lua, "+.Inf", std::numeric_limits::infinity())); ASSERT_TRUE(checkNumber(lua, "-.Inf", -std::numeric_limits::infinity())); ASSERT_TRUE(checkNumber(lua, ".INF", std::numeric_limits::infinity())); ASSERT_TRUE(checkNumber(lua, "+.INF", std::numeric_limits::infinity())); ASSERT_TRUE(checkNumber(lua, "-.INF", -std::numeric_limits::infinity())); ASSERT_TRUE(checkString(lua, ".INf")); ASSERT_TRUE(checkString(lua, "-.INf")); ASSERT_TRUE(checkString(lua, "+.INf")); ASSERT_TRUE(checkBool(lua, "true", true)); ASSERT_TRUE(checkBool(lua, "false", false)); ASSERT_TRUE(checkBool(lua, "True", true)); ASSERT_TRUE(checkBool(lua, "False", false)); ASSERT_TRUE(checkBool(lua, "TRUE", true)); ASSERT_TRUE(checkBool(lua, "FALSE", false)); ASSERT_TRUE(checkString(lua, "y")); ASSERT_TRUE(checkString(lua, "n")); ASSERT_TRUE(checkString(lua, "On")); ASSERT_TRUE(checkString(lua, "Off")); ASSERT_TRUE(checkString(lua, "YES")); ASSERT_TRUE(checkString(lua, "NO")); ASSERT_TRUE(checkString(lua, "TrUe")); ASSERT_TRUE(checkString(lua, "FaLsE")); ASSERT_TRUE(checkString(lua, "'true'", "true")); } TEST(LuaUtilYamlLoader, DepthLimit) { sol::state lua; const std::string input = R"( array1: &array1_alias [ <: *array1_alias, foo ] )"; bool depthExceptionThrown = false; try { YAML::Node root = YAML::Load(input); sol::object result = LuaUtil::loadYaml(input, lua); } catch (const std::runtime_error& e) { ASSERT_EQ(std::string(e.what()), "Maximum layers depth exceeded, probably caused by a circular reference"); depthExceptionThrown = true; } ASSERT_TRUE(depthExceptionThrown); } TEST(LuaUtilYamlLoader, Collections) { sol::state lua; sol::object map = LuaUtil::loadYaml("{ x: , y: 2, 4: 5 }", lua); ASSERT_EQ(map.as()["x"], sol::nil); ASSERT_EQ(map.as()["y"], 2); ASSERT_EQ(map.as()[4], 5); sol::object array = LuaUtil::loadYaml("[ 3, 4 ]", lua); ASSERT_EQ(array.as()[1], 3); sol::object emptyTable = LuaUtil::loadYaml("{}", lua); ASSERT_TRUE(emptyTable.as().empty()); sol::object emptyArray = LuaUtil::loadYaml("[]", lua); ASSERT_TRUE(emptyArray.as().empty()); ASSERT_THROW(LuaUtil::loadYaml("{ null: 1 }", lua), std::runtime_error); ASSERT_THROW(LuaUtil::loadYaml("{ .nan: 1 }", lua), std::runtime_error); const std::string scalarArrayInput = R"( - First Scalar - 1 - true)"; sol::object scalarArray = LuaUtil::loadYaml(scalarArrayInput, lua); ASSERT_EQ(scalarArray.as()[1], std::string("First Scalar")); ASSERT_EQ(scalarArray.as()[2], 1); ASSERT_EQ(scalarArray.as()[3], true); const std::string scalarMapWithCommentsInput = R"( string: 'str' # String value integer: 65 # Integer value float: 0.278 # Float value bool: false # Boolean value)"; sol::object scalarMapWithComments = LuaUtil::loadYaml(scalarMapWithCommentsInput, lua); ASSERT_EQ(scalarMapWithComments.as()["string"], std::string("str")); ASSERT_EQ(scalarMapWithComments.as()["integer"], 65); ASSERT_EQ(scalarMapWithComments.as()["float"], 0.278); ASSERT_EQ(scalarMapWithComments.as()["bool"], false); const std::string mapOfArraysInput = R"( x: - 2 - 7 - true y: - aaa - false - 1)"; sol::object mapOfArrays = LuaUtil::loadYaml(mapOfArraysInput, lua); ASSERT_EQ(mapOfArrays.as()["x"][3], true); ASSERT_EQ(mapOfArrays.as()["y"][1], std::string("aaa")); const std::string arrayOfMapsInput = R"( - name: Name1 hr: 65 avg: 0.278 - name: Name2 hr: 63 avg: 0.288)"; sol::object arrayOfMaps = LuaUtil::loadYaml(arrayOfMapsInput, lua); ASSERT_EQ(arrayOfMaps.as()[1]["avg"], 0.278); ASSERT_EQ(arrayOfMaps.as()[2]["name"], std::string("Name2")); const std::string arrayOfArraysInput = R"( - [Name1, 65, 0.278] - [Name2 , 63, 0.288])"; sol::object arrayOfArrays = LuaUtil::loadYaml(arrayOfArraysInput, lua); ASSERT_EQ(arrayOfArrays.as()[1][2], 65); ASSERT_EQ(arrayOfArrays.as()[2][1], std::string("Name2")); const std::string mapOfMapsInput = R"( Name1: {hr: 65, avg: 0.278} Name2 : { hr: 63, avg: 0.288, })"; sol::object mapOfMaps = LuaUtil::loadYaml(mapOfMapsInput, lua); ASSERT_EQ(mapOfMaps.as()["Name1"]["hr"], 65); ASSERT_EQ(mapOfMaps.as()["Name2"]["avg"], 0.288); } TEST(LuaUtilYamlLoader, Structures) { sol::state lua; const std::string twoDocumentsInput = "---\n" " - First Scalar\n" " - 2\n" " - true\n" "\n" "---\n" " - Second Scalar\n" " - 3\n" " - false"; sol::object twoDocuments = LuaUtil::loadYaml(twoDocumentsInput, lua); ASSERT_EQ(twoDocuments.as()[1][1], std::string("First Scalar")); ASSERT_EQ(twoDocuments.as()[2][3], false); const std::string anchorInput = R"(--- x: - Name1 # Following node labeled as "a" - &a Value1 y: - *a # Subsequent occurrence - Name2)"; sol::object anchor = LuaUtil::loadYaml(anchorInput, lua); ASSERT_EQ(anchor.as()["y"][1], std::string("Value1")); const std::string compoundKeyInput = R"( ? - String1 - String2 : - 1 ? [ String3, String4 ] : [ 2, 3, 4 ])"; ASSERT_THROW(LuaUtil::loadYaml(compoundKeyInput, lua), std::runtime_error); const std::string compactNestedMappingInput = R"( - item : Item1 quantity: 2 - item : Item2 quantity: 4 - item : Item3 quantity: 11)"; sol::object compactNestedMapping = LuaUtil::loadYaml(compactNestedMappingInput, lua); ASSERT_EQ(compactNestedMapping.as()[2]["quantity"], 4); } TEST(LuaUtilYamlLoader, Scalars) { sol::state lua; const std::string literalScalarInput = R"(--- | a b c)"; ASSERT_TRUE(checkString(lua, literalScalarInput, "a\nb\nc")); const std::string foldedScalarInput = R"(--- > a b c)"; ASSERT_TRUE(checkString(lua, foldedScalarInput, "a b c")); const std::string multiLinePlanarScalarsInput = R"( plain: This unquoted scalar spans many lines. quoted: "So does this quoted scalar.\n")"; sol::object multiLinePlanarScalars = LuaUtil::loadYaml(multiLinePlanarScalarsInput, lua); ASSERT_TRUE( multiLinePlanarScalars.as()["plain"] == std::string("This unquoted scalar spans many lines.")); ASSERT_TRUE(multiLinePlanarScalars.as()["quoted"] == std::string("So does this quoted scalar.\n")); } }