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"