File indexing completed on 2024-05-12 05:55:45
0001 /* 0002 * This file is part of the Okteta Kasten module, made within the KDE community. 0003 * 0004 * SPDX-FileCopyrightText: 2011, 2012 Alex Richardson <alex.richardson@gmx.de> 0005 * 0006 * SPDX-License-Identifier: LGPL-2.1-or-later 0007 */ 0008 0009 #include "view/structures/script/scriptengineinitializer.hpp" 0010 #include "view/structures/allprimitivetypes.hpp" 0011 #include "view/structures/parsers/scriptvalueconverter.hpp" 0012 #include "view/structures/datatypes/datainformation.hpp" 0013 #include "view/structures/datatypes/primitive/primitivedatainformation.hpp" 0014 #include "view/structures/datatypes/primitive/enumdatainformation.hpp" 0015 #include "view/structures/script/scriptlogger.hpp" 0016 #include "view/structures/parsers/parserutils.hpp" 0017 // Qt 0018 #include <QTest> 0019 #include <QString> 0020 #include <QDebug> 0021 #include <QScriptEngine> 0022 // Std 0023 #include <memory> 0024 0025 class ScriptValueConverterTest : public QObject 0026 { 0027 Q_OBJECT 0028 0029 private Q_SLOTS: 0030 void initTestCase(); 0031 void testPrimitives(); 0032 void testPrimitives_data(); 0033 void testParseEnum(); 0034 void basicConverterTest(); 0035 void testParseEnum_data(); 0036 0037 private: 0038 DataInformation* convert(const QString& code); 0039 DataInformation* convert(const QScriptValue& value); 0040 QScriptValue evaluate(const char* code); 0041 void dumpLoggerOutput(); 0042 std::unique_ptr<QScriptEngine> engine; 0043 std::unique_ptr<ScriptLogger> logger; 0044 }; 0045 0046 DataInformation* ScriptValueConverterTest::convert(const QString& code) 0047 { 0048 QScriptValue value = engine->evaluate(code); 0049 return ScriptValueConverter::convert(value, QStringLiteral("value"), logger.get()); 0050 } 0051 0052 DataInformation* ScriptValueConverterTest::convert(const QScriptValue& value) 0053 { 0054 return ScriptValueConverter::convert(value, QStringLiteral("value"), logger.get()); 0055 } 0056 0057 QScriptValue ScriptValueConverterTest::evaluate(const char* code) 0058 { 0059 return engine->evaluate(QString::fromUtf8(code)); 0060 } 0061 0062 void ScriptValueConverterTest::initTestCase() 0063 { 0064 engine.reset(ScriptEngineInitializer::newEngine()); 0065 logger.reset(new ScriptLogger()); 0066 } 0067 0068 void ScriptValueConverterTest::basicConverterTest() 0069 { 0070 logger->clear(); 0071 // check that passing functions works 0072 QScriptValue sVal = evaluate("var foo = { value : uint8(),\n" 0073 " str : struct({first : uint8(), second : uint16()}),\n" 0074 " obj : array(uint32(), 10) \n}\n foo"); 0075 QVector<DataInformation*> converted = ScriptValueConverter::convertValues(sVal, logger.get()); 0076 QCOMPARE(converted.size(), 3); 0077 QVERIFY(converted[0]->isPrimitive()); 0078 QCOMPARE(converted[0]->name(), QStringLiteral("value")); 0079 QVERIFY(converted[1]->isStruct()); 0080 QCOMPARE(converted[1]->name(), QStringLiteral("str")); 0081 QCOMPARE(converted[1]->childCount(), 2U); 0082 QVERIFY(converted[2]->isArray()); 0083 QCOMPARE(converted[2]->name(), QStringLiteral("obj")); 0084 QCOMPARE(converted[2]->childCount(), 10U); 0085 0086 // test with an array now 0087 sVal = evaluate("var foo = [uint8(), uint16(), uint32()]; foo"); 0088 qDeleteAll(converted); 0089 converted = ScriptValueConverter::convertValues(sVal, logger.get()); 0090 QCOMPARE(converted.size(), 3); 0091 QVERIFY(converted[0]->isPrimitive()); 0092 QVERIFY(converted[1]->isPrimitive()); 0093 QVERIFY(converted[2]->isPrimitive()); 0094 QVERIFY(converted[0]->asPrimitive()->type() == PrimitiveDataType::UInt8); 0095 QVERIFY(converted[1]->asPrimitive()->type() == PrimitiveDataType::UInt16); 0096 QVERIFY(converted[2]->asPrimitive()->type() == PrimitiveDataType::UInt32); 0097 QCOMPARE(converted[0]->name(), QStringLiteral("0")); 0098 QCOMPARE(converted[1]->name(), QStringLiteral("1")); 0099 QCOMPARE(converted[2]->name(), QStringLiteral("2")); 0100 0101 // check number is not a valid object 0102 sVal = evaluate("1 + 2"); 0103 QVERIFY(sVal.isNumber()); 0104 QVERIFY2(!convert(sVal), " numbers should not be valid!"); 0105 QCOMPARE(logger->rowCount(QModelIndex()), 1); 0106 0107 // should be exactly 1 error message 0108 sVal = evaluate("function foo() { return uint8(); }; foo"); 0109 QVERIFY(sVal.isFunction()); 0110 QVERIFY2(!convert(sVal), "functions should not be valid!"); 0111 QCOMPARE(logger->rowCount(QModelIndex()), 2); 0112 0113 // should be exactly 2 error messages 0114 sVal = evaluate("var x = /.*/; x"); 0115 QVERIFY(sVal.isRegExp()); 0116 QVERIFY2(!convert(sVal), " regexp should not be valid!"); 0117 QCOMPARE(logger->rowCount(QModelIndex()), 3); 0118 0119 sVal = evaluate("var obj = { x : 1 }; obj.x();"); 0120 QVERIFY(sVal.isError()); 0121 QVERIFY2(!convert(sVal), " error objects should not be valid!"); 0122 QCOMPARE(logger->rowCount(QModelIndex()), 4); 0123 0124 sVal = evaluate("var x = [1, 2, 3]; x"); 0125 QVERIFY(sVal.isArray()); 0126 QVERIFY2(!convert(sVal), " array objects should not be valid!"); 0127 QCOMPARE(logger->rowCount(QModelIndex()), 5); 0128 0129 sVal = evaluate("var x = new Date(); x"); 0130 QVERIFY(sVal.isDate()); 0131 QVERIFY2(!convert(sVal), " date objects should not be valid!"); 0132 QCOMPARE(logger->rowCount(QModelIndex()), 6); 0133 0134 sVal = evaluate("var x = true; x"); 0135 QVERIFY(sVal.isBool()); 0136 QVERIFY2(!convert(sVal), " bool objects should not be valid!"); 0137 QCOMPARE(logger->rowCount(QModelIndex()), 7); 0138 0139 sVal = evaluate("var x = null; x"); 0140 QVERIFY(sVal.isNull()); 0141 QVERIFY2(!convert(sVal), " null should not be valid!"); 0142 QCOMPARE(logger->rowCount(QModelIndex()), 8); 0143 0144 sVal = evaluate("var x = undefined; x"); 0145 QVERIFY(sVal.isUndefined()); 0146 QVERIFY2(!convert(sVal), " undefined should not be valid!"); 0147 QCOMPARE(logger->rowCount(QModelIndex()), 9); 0148 0149 // object with invalid entry 0150 sVal = evaluate("var foo = { value : function() { return 1; },\n" 0151 " str : struct({first : uint8(), second : uint16()}),\n" 0152 " obj : array(uint32(), 10) \n}\n foo"); 0153 qDeleteAll(converted); 0154 converted = ScriptValueConverter::convertValues(sVal, logger.get()); 0155 QCOMPARE(converted.size(), 2); 0156 // first entry is invalid 0157 QCOMPARE(logger->rowCount(QModelIndex()), 11); 0158 // this should cause 2 error messages -> 11 now 0159 // qDebug() << logger->messages(); 0160 qDeleteAll(converted); 0161 } 0162 0163 void ScriptValueConverterTest::testPrimitives_data() 0164 { 0165 QTest::addColumn<QString>("code"); 0166 QTest::addColumn<QString>("code2"); 0167 QTest::addColumn<QString>("typeString"); 0168 QTest::addColumn<int>("expectedType"); 0169 0170 QTest::newRow("uint8") << "uint8()" << "new uint8()" << "UInt8" << (int) PrimitiveDataType::UInt8; 0171 QTest::newRow("uint16") << "uint16()" << "new uint16()" << "UInt16" << (int) PrimitiveDataType::UInt16; 0172 QTest::newRow("uint32") << "uint32()" << "new uint32()" << "UInt32" << (int) PrimitiveDataType::UInt32; 0173 QTest::newRow("uint64") << "uint64()" << "new uint64()" << "UInt64" << (int) PrimitiveDataType::UInt64; 0174 QTest::newRow("int8") << "int8()" << "new int8()" << "Int8" << (int) PrimitiveDataType::Int8; 0175 QTest::newRow("int16") << "int16()" << "new int16()" << "Int16" << (int) PrimitiveDataType::Int16; 0176 QTest::newRow("int32") << "int32()" << "new int32()" << "Int32" << (int) PrimitiveDataType::Int32; 0177 QTest::newRow("int64") << "int64()" << "new int64()" << "Int64" << (int) PrimitiveDataType::Int64; 0178 QTest::newRow("bool8") << "bool8()" << "new bool8()" << "Bool8" << (int) PrimitiveDataType::Bool8; 0179 QTest::newRow("bool16") << "bool16()" << "new bool16()" << "Bool16" << (int) PrimitiveDataType::Bool16; 0180 QTest::newRow("bool32") << "bool32()" << "new bool32()" << "Bool32" << (int) PrimitiveDataType::Bool32; 0181 QTest::newRow("bool64") << "bool64()" << "new bool64()" << "Bool64" << (int) PrimitiveDataType::Bool64; 0182 QTest::newRow("char") << "char()" << "new char()" << "Char" << (int) PrimitiveDataType::Char; 0183 QTest::newRow("float") << "float()" << "new float()" << "Float" << (int) PrimitiveDataType::Float; 0184 QTest::newRow("double") << "double()" << "new double()" << "Double" << (int) PrimitiveDataType::Double; 0185 } 0186 0187 void ScriptValueConverterTest::testPrimitives() 0188 { 0189 QFETCH(QString, code); 0190 QFETCH(QString, code2); 0191 QFETCH(QString, typeString); 0192 QFETCH(int, expectedType); 0193 logger->clear(); 0194 auto type = static_cast<PrimitiveDataType>(expectedType); 0195 0196 QScriptValue val1 = engine->evaluate(code); 0197 QScriptValue val2 = engine->evaluate(code2); 0198 QCOMPARE(val1.property(ParserStrings::PROPERTY_TYPE()).toString(), typeString); 0199 QCOMPARE(val2.property(ParserStrings::PROPERTY_TYPE()).toString(), typeString); 0200 QCOMPARE(val1.property(ParserStrings::PROPERTY_INTERNAL_TYPE()).toString(), ParserStrings::TYPE_PRIMITIVE()); 0201 QCOMPARE(val2.property(ParserStrings::PROPERTY_INTERNAL_TYPE()).toString(), ParserStrings::TYPE_PRIMITIVE()); 0202 0203 if (type == PrimitiveDataType::Invalid) { 0204 return; // the cast will fail 0205 } 0206 std::unique_ptr<DataInformation> data1(ScriptValueConverter::convert(val1, QStringLiteral("val1"), 0207 logger.get())); 0208 std::unique_ptr<DataInformation> data2(ScriptValueConverter::convert(val2, QStringLiteral("val2"), 0209 logger.get())); 0210 QVERIFY(data1); 0211 QVERIFY(data2); 0212 PrimitiveDataInformation* p1 = data1->asPrimitive(); 0213 PrimitiveDataInformation* p2 = data2->asPrimitive(); 0214 QVERIFY(p1); 0215 QVERIFY(p2); 0216 QCOMPARE(p1->type(), type); 0217 QCOMPARE(p2->type(), type); 0218 if (type == PrimitiveDataType::Bitfield) { 0219 return; // the following tests don't work with bitfields 0220 } 0221 std::unique_ptr<DataInformation> data3(convert(QStringLiteral("\"%1\"").arg(typeString))); 0222 QVERIFY(data3); 0223 PrimitiveDataInformation* p3 = data3->asPrimitive(); 0224 QVERIFY(p3); 0225 QCOMPARE(p3->type(), type); 0226 } 0227 0228 void ScriptValueConverterTest::testParseEnum() 0229 { 0230 // QFETCH(QString, name); 0231 QFETCH(QString, code); 0232 QFETCH(int, expectedCount); 0233 0234 QScriptValue val = engine->evaluate(code); 0235 0236 QVERIFY(val.isValid()); 0237 QVERIFY(!val.isNull()); 0238 QVERIFY(!val.isUndefined()); 0239 QVERIFY(val.isObject()); 0240 QCOMPARE(val.property(ParserStrings::PROPERTY_INTERNAL_TYPE()).toString(), QStringLiteral("enum")); 0241 0242 std::unique_ptr<DataInformation> data(ScriptValueConverter::convert(val, QStringLiteral("val"), logger.get())); 0243 if (expectedCount > 0) { 0244 QVERIFY(data); 0245 } else { 0246 QVERIFY(!data); 0247 return; 0248 } 0249 EnumDataInformation* e = data->asEnum(); 0250 QVERIFY(e); 0251 0252 QMap<AllPrimitiveTypes, QString> enumVals = e->enumValues()->values(); 0253 QCOMPARE(enumVals.size(), expectedCount); 0254 0255 if (expectedCount != 0) { 0256 QFETCH(quint64, expectedValue); 0257 // to ensure it does not match when value is not found add 1 to the default 0258 AllPrimitiveTypes result = enumVals.key(QStringLiteral("value"), expectedValue + 1); 0259 QCOMPARE(result.value<quint64>(), expectedValue); 0260 } 0261 } 0262 0263 namespace { 0264 inline QString arg2(const QString& str, const char* arg_1, const char* arg_2) 0265 { 0266 return str.arg(QString::fromUtf8(arg_1), QString::fromUtf8(arg_2)); 0267 } 0268 } 0269 0270 void ScriptValueConverterTest::testParseEnum_data() 0271 { 0272 QString baseStr = QStringLiteral("enumeration(\"someValues\", %1, { value : %2})"); 0273 QTest::addColumn<QString>("code"); 0274 QTest::addColumn<int>("expectedCount"); 0275 QTest::addColumn<quint64>("expectedValue"); 0276 0277 QTest::newRow("invalid_type_struct") << arg2(baseStr, "struct({ val : uint8() })", "1234.234") 0278 << 0 << quint64(0); 0279 QTest::newRow("invalid_type_array") << arg2(baseStr, "array(uint8(), 1)", "1234.234") << 0 0280 << quint64(0); 0281 QTest::newRow("invalid_type_union") << arg2(baseStr, "union({ val : uint8() })", "1234.234") 0282 << 0 << quint64(0); 0283 QTest::newRow("invalid_type_double") << arg2(baseStr, "double()", "1234.234") << 0 << quint64(0); 0284 QTest::newRow("invalid_type_float") << arg2(baseStr, "float()", "1234.234") << 0 << quint64(0); 0285 QTest::newRow("invalid_type_string") << arg2(baseStr, "string()", "1234.234") << 0 << quint64(0); 0286 0287 QTest::newRow("float2int8") << arg2(baseStr, "uint8()", "1.234") << 1 << quint64(1); 0288 QTest::newRow("float2int8_range") << arg2(baseStr, "uint8()", "1234.234") << 0 << quint64(0); 0289 QTest::newRow("float2int32") << arg2(baseStr, "uint32()", "1234.1234") << 1 << quint64(1234); 0290 QTest::newRow("float2int32_range") << arg2(baseStr, "uint32()", "5294967296.234") << 0 << quint64(0); 0291 QTest::newRow("float2int64") << arg2(baseStr, "uint64()", "5294967296.234") << 1 0292 << quint64(5294967296UL); 0293 QTest::newRow("double_overflow") << arg2(baseStr, "uint64()", "9007199254740993.0") << 0 0294 << quint64(9007199254740993UL); // only 992 and 994 can be represented as a double 0295 QTest::newRow("uint64_max_hex") << arg2(baseStr, "uint64()", "new String(\"0xFFFFFFFFFFFFFFFF\")") << 1 0296 << quint64(0xFFFFFFFFFFFFFFFFL); 0297 QTest::newRow("uint64_max") << arg2(baseStr, "uint64()", "new String(\"18446744073709551615\")") << 1 0298 << quint64(18446744073709551615UL); 0299 } 0300 0301 QTEST_GUILESS_MAIN(ScriptValueConverterTest) 0302 0303 #include "scriptvalueconvertertest.moc"