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