Warning, file /utilities/okteta/kasten/controllers/test/jsparsertest.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 * This file is part of the Okteta Kasten module, made within the KDE community. 0003 * 0004 * SPDX-FileCopyrightText: 2013 Alex Richardson <alex.richardson@gmx.de> 0005 * 0006 * SPDX-License-Identifier: LGPL-2.1-or-later 0007 */ 0008 0009 // TODO: find better way to work-around simple name creation for QTest::newRow 0010 #undef QT_USE_QSTRINGBUILDER 0011 0012 #include <QTest> 0013 #include <QScriptEngine> 0014 #include "view/structures/script/scriptengineinitializer.hpp" 0015 #include "view/structures/parsers/scriptvalueconverter.hpp" 0016 #include "testutils.hpp" 0017 #include <functional> 0018 0019 struct JsTestData 0020 { 0021 using CheckCallback = std::function<void(DataInformation*)>; 0022 JsTestData() = default; 0023 JsTestData(const char* tag, const char* constructor, const CheckCallback& check) 0024 : tag(tag) 0025 , constructorCall(QString::fromUtf8(constructor)) 0026 , check(check) 0027 {} 0028 QByteArray tag; 0029 QString constructorCall; 0030 CheckCallback check; 0031 }; 0032 Q_DECLARE_METATYPE(JsTestData) 0033 Q_DECLARE_METATYPE(JsTestData::CheckCallback) 0034 0035 class JsParserTest : public QObject 0036 { 0037 Q_OBJECT 0038 0039 private Q_SLOTS: 0040 void initTestCase(); 0041 void testValidationFunc(); 0042 void testValidationFunc_data(); 0043 void testUpdateFunc(); 0044 void testUpdateFunc_data(); 0045 void testByteOrder(); 0046 void testByteOrder_data(); 0047 void testName(); 0048 void testName_data(); 0049 void testCustomTypeName(); 0050 void testCustomTypeName_data(); 0051 void testImport(); 0052 void testImportPathTraversal(); 0053 0054 private: 0055 /** data gets set to the parsed result. 0056 * This is needed since functions with QVERIFY/QCOMPARE must return void */ 0057 void testCommon(DataInformation** data); 0058 0059 private: 0060 QScriptEngine engine; 0061 QVector<JsTestData> primitiveData; 0062 QVector<JsTestData> bitfieldData; 0063 QVector<JsTestData> allData; 0064 }; 0065 0066 static JsTestData::CheckCallback primitiveTypeCheck(PrimitiveDataType type) 0067 { 0068 return [type](DataInformation* data) { 0069 QVERIFY(data->isPrimitive()); 0070 QCOMPARE(data->asPrimitive()->type(), type); 0071 }; 0072 } 0073 0074 static JsTestData::CheckCallback bitfieldCheck(AbstractBitfieldDataInformation::Type type) 0075 { 0076 return [type](DataInformation* data) { 0077 QVERIFY(data->isPrimitive()); 0078 QCOMPARE(data->asBitfield()->bitfieldType(), type); 0079 }; 0080 } 0081 0082 void JsParserTest::initTestCase() 0083 { 0084 ScriptEngineInitializer::addFuctionsToScriptEngine(&engine); 0085 0086 primitiveData 0087 << JsTestData("float", "float()", primitiveTypeCheck(PrimitiveDataType::Float)) 0088 << JsTestData("double", "double()", primitiveTypeCheck(PrimitiveDataType::Double)) 0089 << JsTestData("char", "char()", primitiveTypeCheck(PrimitiveDataType::Char)) 0090 0091 << JsTestData("uint8", "uint8()", primitiveTypeCheck(PrimitiveDataType::UInt8)) 0092 << JsTestData("uint16", "uint16()", primitiveTypeCheck(PrimitiveDataType::UInt16)) 0093 << JsTestData("uint32", "uint32()", primitiveTypeCheck(PrimitiveDataType::UInt32)) 0094 << JsTestData("uint64", "uint64()", primitiveTypeCheck(PrimitiveDataType::UInt64)) 0095 0096 << JsTestData("int8", "int8()", primitiveTypeCheck(PrimitiveDataType::Int8)) 0097 << JsTestData("int16", "int16()", primitiveTypeCheck(PrimitiveDataType::Int16)) 0098 << JsTestData("int32", "int32()", primitiveTypeCheck(PrimitiveDataType::Int32)) 0099 << JsTestData("int64", "int64()", primitiveTypeCheck(PrimitiveDataType::Int64)) 0100 0101 << JsTestData("bool8", "bool8()", primitiveTypeCheck(PrimitiveDataType::Bool8)) 0102 << JsTestData("bool16", "bool16()", primitiveTypeCheck(PrimitiveDataType::Bool16)) 0103 << JsTestData("bool32", "bool32()", primitiveTypeCheck(PrimitiveDataType::Bool32)) 0104 << JsTestData("bool64", "bool64()", primitiveTypeCheck(PrimitiveDataType::Bool64)); 0105 0106 bitfieldData 0107 << JsTestData("signed bitfield", "bitfield(\"signed\", 5)", 0108 bitfieldCheck(AbstractBitfieldDataInformation::Type::Signed)) 0109 << JsTestData("unsigned bitfield", "bitfield(\"unsigned\", 5)", 0110 bitfieldCheck(AbstractBitfieldDataInformation::Type::Unsigned)) 0111 << JsTestData("bool bitfield", "bitfield(\"bool\", 5)", 0112 bitfieldCheck(AbstractBitfieldDataInformation::Type::Boolean)); 0113 0114 allData << primitiveData << bitfieldData; 0115 // TODO struct, union, taggedUnion, pointer, flags, enum, array, string 0116 0117 // needed so that imports can be resolved 0118 QString resources = QFINDTESTDATA("resources"); 0119 QString examples = QFINDTESTDATA("../view/structures/examples"); 0120 QVERIFY2(!resources.isEmpty(), "Test data must exist!"); 0121 QVERIFY2(!examples.isEmpty(), "Test data must exist!"); 0122 qputenv("XDG_DATA_DIRS", 0123 QFile::encodeName(QFileInfo(resources).absoluteFilePath()) + ':' + 0124 QFile::encodeName(QFileInfo(examples).absoluteFilePath())); 0125 } 0126 0127 void JsParserTest::testByteOrder_data() 0128 { 0129 QTest::addColumn<QString>("code"); 0130 QTest::addColumn<JsTestData::CheckCallback>("checkFunction"); 0131 QTest::addColumn<int>("expectedByteOrder"); 0132 // verify that default is inherit 0133 for (const JsTestData& data : qAsConst(allData)) { 0134 // default should be inherit 0135 QString codeStr = QStringLiteral("%1;"); 0136 QTest::newRow(data.tag.constData()) << codeStr.arg(data.constructorCall) 0137 << data.check << (int)DataInformation::DataInformationEndianess::EndianessInherit; 0138 0139 // use set() function to specify byteOrder 0140 codeStr = QStringLiteral("%1.set({byteOrder: \"inherit\"})"); 0141 QTest::newRow((data.tag + " set() inherit").constData()) << codeStr.arg(data.constructorCall) 0142 << data.check << (int)DataInformation::DataInformationEndianess::EndianessInherit; 0143 codeStr = QStringLiteral("%1.set({byteOrder: \"littleEndian\"})"); 0144 QTest::newRow((data.tag + " set() little endian").constData()) << codeStr.arg(data.constructorCall) 0145 << data.check << (int)DataInformation::DataInformationEndianess::EndianessLittle; 0146 codeStr = QStringLiteral("%1.set({byteOrder: \"bigEndian\"})"); 0147 QTest::newRow((data.tag + " set() big endian").constData()) << codeStr.arg(data.constructorCall) 0148 << data.check << (int)DataInformation::DataInformationEndianess::EndianessBig; 0149 codeStr = QStringLiteral("%1.set({byteOrder: \"fromSettings\"})"); 0150 QTest::newRow((data.tag + " set() from settings").constData()) << codeStr.arg(data.constructorCall) 0151 << data.check << (int)DataInformation::DataInformationEndianess::EndianessFromSettings; 0152 0153 // direct property access to specify byteOrder 0154 codeStr = QStringLiteral("var obj = %1; obj.byteOrder = \"inherit\"; obj;"); 0155 QTest::newRow((data.tag + " property assign inherit").constData()) << codeStr.arg(data.constructorCall) 0156 << data.check << (int)DataInformation::DataInformationEndianess::EndianessInherit; 0157 codeStr = QStringLiteral("var obj = %1; obj.byteOrder = \"little-endian\"; obj;"); 0158 QTest::newRow((data.tag + " property assign little endian").constData()) << codeStr.arg(data.constructorCall) 0159 << data.check << (int)DataInformation::DataInformationEndianess::EndianessLittle; 0160 codeStr = QStringLiteral("var obj = %1; obj.byteOrder = \"big-endian\"; obj;"); 0161 QTest::newRow((data.tag + " property assign big endian").constData()) << codeStr.arg(data.constructorCall) 0162 << data.check << (int)DataInformation::DataInformationEndianess::EndianessBig; 0163 codeStr = QStringLiteral("var obj = %1; obj.byteOrder = \"from-settings\"; obj;"); 0164 QTest::newRow((data.tag + " property assign from settings").constData()) << codeStr.arg(data.constructorCall) 0165 << data.check << (int)DataInformation::DataInformationEndianess::EndianessFromSettings; 0166 } 0167 } 0168 0169 void JsParserTest::testCommon(DataInformation** dataPtr) 0170 { 0171 QFETCH(QString, code); 0172 QFETCH(JsTestData::CheckCallback, checkFunction); 0173 QScriptValue value = engine.evaluate(code); 0174 QVERIFY(value.isValid()); 0175 QVERIFY(!value.isError()); 0176 QVERIFY(value.isObject()); 0177 ScriptLogger logger; 0178 QScopedPointer<DataInformation> data 0179 (ScriptValueConverter::convert(value, QStringLiteral("converted"), &logger)); 0180 QVERIFY(logger.rowCount() == 0); 0181 QVERIFY(data); 0182 checkFunction(data.data()); 0183 *dataPtr = data.take(); 0184 } 0185 0186 void JsParserTest::testByteOrder() 0187 { 0188 DataInformation* data = nullptr; 0189 testCommon(&data); 0190 if (QTest::currentTestFailed()) { 0191 return; // Qt doesn't use exceptions, we must manually check after each call 0192 } 0193 QFETCH(int, expectedByteOrder); 0194 QCOMPARE((int)data->byteOrder(), expectedByteOrder); 0195 } 0196 0197 namespace { 0198 QString updateFunction() { return QStringLiteral("function () { /* do nothing*/; }"); } 0199 } 0200 0201 void JsParserTest::testUpdateFunc_data() 0202 { 0203 QTest::addColumn<QString>("code"); 0204 QTest::addColumn<JsTestData::CheckCallback>("checkFunction"); 0205 0206 for (const JsTestData& data : qAsConst(allData)) { 0207 QString codeStr = QStringLiteral("%1.setUpdate(") + updateFunction() + QStringLiteral(");"); 0208 QTest::newRow((data.tag + "-setUpdate()").constData()) 0209 << codeStr.arg(data.constructorCall) << data.check; 0210 0211 codeStr = QStringLiteral("%1.set({updateFunc: ") + updateFunction() + QStringLiteral("});"); 0212 QTest::newRow((data.tag + "-set()").constData()) 0213 << codeStr.arg(data.constructorCall) << data.check; 0214 0215 codeStr = QStringLiteral("var obj = %1; obj.updateFunc = ") + updateFunction() + QStringLiteral("; obj;"); 0216 QTest::newRow((data.tag + "-property assign").constData()) 0217 << codeStr.arg(data.constructorCall) << data.check; 0218 } 0219 } 0220 0221 void JsParserTest::testUpdateFunc() 0222 { 0223 DataInformation* data = nullptr; 0224 testCommon(&data); 0225 if (QTest::currentTestFailed()) { 0226 return; // Qt doesn't use exceptions, we must manually check after each call 0227 } 0228 QVERIFY(data); 0229 QScriptValue update = data->updateFunc(); 0230 QVERIFY(update.isValid()); 0231 QVERIFY(update.isFunction()); 0232 QCOMPARE(update.toString(), updateFunction()); 0233 } 0234 0235 namespace { 0236 QString validationFunction() { return QStringLiteral("function () { return true; }"); } 0237 } 0238 0239 void JsParserTest::testValidationFunc_data() 0240 { 0241 QTest::addColumn<QString>("code"); 0242 QTest::addColumn<JsTestData::CheckCallback>("checkFunction"); 0243 0244 for (const JsTestData& data : qAsConst(allData)) { 0245 QString codeStr = QStringLiteral("%1.setValidation(") + validationFunction() + QStringLiteral(");"); 0246 QTest::newRow((data.tag + "-setUpdate()").constData()) 0247 << codeStr.arg(data.constructorCall) << data.check; 0248 0249 codeStr = QStringLiteral("%1.set({validationFunc: ") + validationFunction() + QStringLiteral("});"); 0250 QTest::newRow((data.tag + "-set()").constData()) 0251 << codeStr.arg(data.constructorCall) << data.check; 0252 0253 codeStr = QStringLiteral("var obj = %1; obj.validationFunc = ") + validationFunction() + QStringLiteral("; obj;"); 0254 QTest::newRow((data.tag + "-property assign").constData()) 0255 << codeStr.arg(data.constructorCall) << data.check; 0256 } 0257 } 0258 0259 void JsParserTest::testValidationFunc() 0260 { 0261 DataInformation* data = nullptr; 0262 testCommon(&data); 0263 if (QTest::currentTestFailed()) { 0264 return; // Qt doesn't use exceptions, we must manually check after each call 0265 0266 } 0267 QScriptValue validation = data->validationFunc(); 0268 QVERIFY(validation.isValid()); 0269 QVERIFY(validation.isFunction()); 0270 QCOMPARE(validation.toString(), validationFunction()); 0271 } 0272 0273 void JsParserTest::testName_data() 0274 { 0275 QTest::addColumn<QString>("code"); 0276 QTest::addColumn<JsTestData::CheckCallback>("checkFunction"); 0277 0278 for (const JsTestData& data : qAsConst(allData)) { 0279 QString codeStr = QStringLiteral("%1.set({name: \"expectedName\"});"); 0280 QTest::newRow((data.tag + "-set()").constData()) 0281 << codeStr.arg(data.constructorCall) << data.check; 0282 0283 codeStr = QStringLiteral("var obj = %1;obj.name = \"expectedName\"; obj;"); 0284 QTest::newRow((data.tag + "-property assignment").constData()) 0285 << codeStr.arg(data.constructorCall) << data.check; 0286 } 0287 } 0288 0289 void JsParserTest::testName() 0290 { 0291 DataInformation* data = nullptr; 0292 testCommon(&data); 0293 if (QTest::currentTestFailed()) { 0294 return; // Qt doesn't use exceptions, we must manually check after each call 0295 0296 } 0297 QCOMPARE(data->name(), QStringLiteral("expectedName")); 0298 } 0299 0300 void JsParserTest::testCustomTypeName_data() 0301 { 0302 QTest::addColumn<QString>("code"); 0303 QTest::addColumn<JsTestData::CheckCallback>("checkFunction"); 0304 0305 for (const JsTestData& data : qAsConst(allData)) { 0306 QString codeStr = QStringLiteral("%1.set({typeName: 'myCustomType'});"); 0307 QTest::newRow((data.tag + "-set()").constData()) 0308 << codeStr.arg(data.constructorCall) << data.check; 0309 0310 codeStr = QStringLiteral("var obj = %1;obj.typeName = 'myCustomType'; obj;"); 0311 QTest::newRow((data.tag + "-property assignment").constData()) 0312 << codeStr.arg(data.constructorCall) << data.check; 0313 } 0314 } 0315 0316 void JsParserTest::testCustomTypeName() 0317 { 0318 DataInformation* data = nullptr; 0319 testCommon(&data); 0320 if (QTest::currentTestFailed()) { 0321 return; // Qt doesn't use exceptions, we must manually check after each call 0322 0323 } 0324 QCOMPARE(data->typeName(), QStringLiteral("myCustomType")); 0325 } 0326 0327 void JsParserTest::testImport() 0328 { 0329 QScopedPointer<QScriptEngine> eng(ScriptEngineInitializer::newEngine()); 0330 #if defined(Q_OS_MACOS) || defined(Q_OS_WINDOWS) 0331 QEXPECT_FAIL("", "QStandardPaths::GenericDataLocation can't be modified on macOS/Windows", Continue); 0332 #endif 0333 QScriptValue val = eng->evaluate(QStringLiteral("s = importScript('simpleImport.js');s.foo()")); 0334 QCOMPARE(val.toString(), QStringLiteral("100")); 0335 } 0336 0337 void JsParserTest::testImportPathTraversal() 0338 { 0339 QScopedPointer<QScriptEngine> eng(ScriptEngineInitializer::newEngine()); 0340 QScriptValue val = eng->evaluate(QStringLiteral("s = importScript('../../pathtraversal.js');s.foo()")); 0341 QVERIFY(val.isError()); 0342 QCOMPARE(val.toString(), QStringLiteral("Error: importScript(): You may only access installed structure files! Path traversal detected.")); 0343 } 0344 0345 QTEST_GUILESS_MAIN(JsParserTest) 0346 0347 #include "jsparsertest.moc"