Warning, file /utilities/okteta/kasten/controllers/test/scriptclassestest.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: 2012, 2013 Alex Richardson <alex.richardson@gmx.de> 0005 * 0006 * SPDX-License-Identifier: LGPL-2.1-or-later 0007 */ 0008 0009 #include <QTest> 0010 0011 #include <QObject> 0012 #include <QScriptValueIterator> 0013 #include <QScriptEngine> 0014 0015 #include "view/structures/datatypes/primitive/primitivetemplateinfo.hpp" 0016 #include "view/structures/datatypes/primitive/primitivedatainformation.hpp" 0017 #include "view/structures/datatypes/primitive/enumdatainformation.hpp" 0018 #include "view/structures/datatypes/primitive/flagdatainformation.hpp" 0019 #include "view/structures/datatypes/primitive/pointerdatainformation.hpp" 0020 #include "view/structures/datatypes/primitive/bitfield/boolbitfielddatainformation.hpp" 0021 #include "view/structures/datatypes/primitive/bitfield/unsignedbitfielddatainformation.hpp" 0022 #include "view/structures/datatypes/primitive/bitfield/signedbitfielddatainformation.hpp" 0023 #include "view/structures/datatypes/primitivefactory.hpp" 0024 #include "view/structures/datatypes/topleveldatainformation.hpp" 0025 #include "view/structures/datatypes/strings/stringdatainformation.hpp" 0026 #include "view/structures/datatypes/array/arraydatainformation.hpp" 0027 #include "view/structures/datatypes/structuredatainformation.hpp" 0028 #include "view/structures/datatypes/uniondatainformation.hpp" 0029 #include "view/structures/script/scripthandler.hpp" 0030 #include "view/structures/script/classes/arrayscriptclass.hpp" 0031 #include "view/structures/script/classes/primitivescriptclass.hpp" 0032 #include "view/structures/script/classes/structunionscriptclass.hpp" 0033 #include "view/structures/script/classes/stringscriptclass.hpp" 0034 #include "view/structures/script/classes/enumscriptclass.hpp" 0035 #include "view/structures/script/classes/bitfieldscriptclass.hpp" 0036 #include "view/structures/script/classes/pointerscriptclass.hpp" 0037 #include "view/structures/script/scriptengineinitializer.hpp" 0038 #include "view/structures/script/classes/defaultscriptclass.hpp" 0039 #include "view/structures/script/safereference.hpp" 0040 #include "view/structures/parsers/scriptvalueconverter.hpp" 0041 #include "testutils.hpp" 0042 0043 class ScriptClassesTest : public QObject 0044 { 0045 Q_OBJECT 0046 0047 using PropertyPair = QPair<QString, QScriptValue::PropertyFlags>; 0048 0049 private: 0050 static void checkProperties(const QVector<PropertyPair>& expected, DataInformation* data, 0051 const char* tag); 0052 static PropertyPair pair(const char* name, 0053 QScriptValue::PropertyFlags flags = QScriptValue::Undeletable | QScriptValue::ReadOnly) 0054 { 0055 return PropertyPair(QString::fromUtf8(name), flags); 0056 } 0057 0058 private Q_SLOTS: 0059 void initTestCase(); 0060 // check that all properties are available in the iterator 0061 void checkIterators(); 0062 void testReplaceObject(); // check replacing datatype 0063 void cleanupTestCase(); 0064 void testSafeReferenceDeleteObject(); 0065 void testSafePrimitiveArrayReference(); 0066 void testScriptValueContents_data(); 0067 void testScriptValueContents(); 0068 0069 private: 0070 QVector<PropertyPair> commonProperties; 0071 QVector<PropertyPair> primitiveProperties; 0072 QVector<TopLevelDataInformation*> primitives; 0073 0074 QVector<PropertyPair> enumProperties; 0075 EnumDataInformation* enumData; 0076 QScopedPointer<TopLevelDataInformation> enumDataTop; 0077 FlagDataInformation* flagData; 0078 QScopedPointer<TopLevelDataInformation> flagDataTop; 0079 0080 QVector<PropertyPair> bitfieldProperties; 0081 SignedBitfieldDataInformation* signedBitfield; 0082 QScopedPointer<TopLevelDataInformation> signedBitfieldTop; 0083 UnsignedBitfieldDataInformation* unsignedBitfield; 0084 QScopedPointer<TopLevelDataInformation> unsignedBitfieldTop; 0085 BoolBitfieldDataInformation* boolBitfield; 0086 QScopedPointer<TopLevelDataInformation> boolBitfieldTop; 0087 0088 QVector<PropertyPair> structUnionProperties; // without children 0089 StructureDataInformation* structData; 0090 QScopedPointer<TopLevelDataInformation> structDataTop; 0091 UnionDataInformation* unionData; 0092 QScopedPointer<TopLevelDataInformation> unionDataTop; 0093 0094 QVector<PropertyPair> arrayProperties; // without children 0095 ArrayDataInformation* arrayData; 0096 QScopedPointer<TopLevelDataInformation> arrayDataTop; 0097 0098 QVector<PropertyPair> stringProperties; // without children 0099 StringDataInformation* stringData; 0100 QScopedPointer<TopLevelDataInformation> stringDataTop; 0101 0102 }; 0103 0104 void ScriptClassesTest::initTestCase() 0105 { 0106 // we are only testing properties when updating 0107 // TODO fix this 0108 commonProperties << pair("name", QScriptValue::Undeletable) 0109 << pair("wasAbleToRead") << pair("parent") 0110 << pair("valid") 0111 << pair("validationError") 0112 << pair("byteOrder", QScriptValue::Undeletable) 0113 << pair("updateFunc", QScriptValue::Undeletable) 0114 << pair("validationFunc", QScriptValue::Undeletable) 0115 << pair("datatype", QScriptValue::Undeletable) 0116 << pair("typeName", QScriptValue::Undeletable) 0117 << pair("toStringFunc", QScriptValue::Undeletable); 0118 0119 primitiveProperties << commonProperties << pair("value") << pair("char") << pair("int") 0120 << pair("int8") << pair("int16") << pair("int32") << pair("int64") << pair("uint") 0121 << pair("uint8") << pair("uint16") << pair("uint32") << pair("uint64") << pair("bool") 0122 << pair("float") << pair("double") << pair("int64high32") << pair("int64low32") 0123 << pair("uint64high32") << pair("uint64low32") << pair("type"); 0124 std::sort(primitiveProperties.begin(), primitiveProperties.end()); 0125 LoggerWithContext lwc(nullptr, QString()); 0126 PrimitiveDataType type = PrimitiveDataType::START; 0127 while (type < PrimitiveDataType::Bitfield) { 0128 PrimitiveDataInformation* prim = PrimitiveFactory::newInstance(QStringLiteral("prim"), type, lwc); 0129 prim->setValue(10); 0130 primitives << new TopLevelDataInformation(prim); 0131 type = static_cast<PrimitiveDataType>(static_cast<int>(type) + 1); 0132 } 0133 0134 enumProperties << primitiveProperties << pair("enumValues", QScriptValue::Undeletable); 0135 // TODO valueString property (i.e. the current value as enumerator name) 0136 // XXX enumName 0137 std::sort(enumProperties.begin(), enumProperties.end()); 0138 0139 QMap<AllPrimitiveTypes, QString> enumValues; 0140 enumValues.insert(1, QStringLiteral("one")); 0141 enumValues.insert(2, QStringLiteral("tow")); 0142 enumValues.insert(4, QStringLiteral("four")); 0143 EnumDefinition::Ptr enumDef(new EnumDefinition(enumValues, 0144 QStringLiteral("theEnum"), PrimitiveDataType::Int32)); 0145 enumData = new EnumDataInformation(QStringLiteral("enumData"), 0146 PrimitiveFactory::newInstance(QStringLiteral("dummy"), PrimitiveDataType::Int32, lwc), enumDef); 0147 enumDataTop.reset( 0148 new TopLevelDataInformation(enumData, nullptr, ScriptEngineInitializer::newEngine())); 0149 flagData = new FlagDataInformation(QStringLiteral("flagData"), 0150 PrimitiveFactory::newInstance(QStringLiteral("dummy"), PrimitiveDataType::Int32, lwc), enumDef); 0151 flagDataTop.reset( 0152 new TopLevelDataInformation(flagData, nullptr, ScriptEngineInitializer::newEngine())); 0153 0154 bitfieldProperties << primitiveProperties << pair("width", QScriptValue::Undeletable); 0155 std::sort(bitfieldProperties.begin(), bitfieldProperties.end()); 0156 unsignedBitfield = new UnsignedBitfieldDataInformation(QStringLiteral("unsignedBit"), 42); 0157 unsignedBitfieldTop.reset( 0158 new TopLevelDataInformation(unsignedBitfield, nullptr, ScriptEngineInitializer::newEngine())); 0159 signedBitfield = new SignedBitfieldDataInformation(QStringLiteral("signedBit"), 42); 0160 signedBitfieldTop.reset( 0161 new TopLevelDataInformation(signedBitfield, nullptr, ScriptEngineInitializer::newEngine())); 0162 boolBitfield = new BoolBitfieldDataInformation(QStringLiteral("boolBit"), 42); 0163 boolBitfieldTop.reset( 0164 new TopLevelDataInformation(boolBitfield, nullptr, ScriptEngineInitializer::newEngine())); 0165 0166 stringProperties << commonProperties << pair("terminatedBy", QScriptValue::Undeletable) 0167 << pair("byteCount") << pair("maxCharCount", QScriptValue::Undeletable) 0168 << pair("charCount") << pair("encoding", QScriptValue::Undeletable) 0169 << pair("maxByteCount", QScriptValue::Undeletable); 0170 std::sort(stringProperties.begin(), stringProperties.end()); 0171 stringData = new StringDataInformation(QStringLiteral("string"), StringDataInformation::StringType::Latin1); 0172 stringDataTop.reset( 0173 new TopLevelDataInformation(stringData, nullptr, ScriptEngineInitializer::newEngine())); 0174 0175 arrayProperties << commonProperties << pair("length", QScriptValue::Undeletable) 0176 << pair("type", QScriptValue::Undeletable); 0177 std::sort(arrayProperties.begin(), arrayProperties.end()); 0178 arrayData = new ArrayDataInformation(QStringLiteral("array"), 20, 0179 PrimitiveFactory::newInstance(QStringLiteral("inner"), PrimitiveDataType::Int32, lwc)); 0180 arrayDataTop.reset( 0181 new TopLevelDataInformation(arrayData, nullptr, ScriptEngineInitializer::newEngine())); 0182 0183 structUnionProperties << commonProperties << pair("childCount"); 0184 // property children is only writable -> it is not in the iterator 0185 structData = new StructureDataInformation(QStringLiteral("struct")); 0186 structDataTop.reset( 0187 new TopLevelDataInformation(structData, nullptr, ScriptEngineInitializer::newEngine())); 0188 unionData = new UnionDataInformation(QStringLiteral("union")); 0189 unionDataTop.reset( 0190 new TopLevelDataInformation(unionData, nullptr, ScriptEngineInitializer::newEngine())); 0191 std::sort(structUnionProperties.begin(), structUnionProperties.end()); 0192 0193 } 0194 0195 Q_DECLARE_METATYPE(QScriptClass*) 0196 0197 static inline void scriptValueContentsAddRow(const char* tag, DataInformation* data, QScriptClass* cls) 0198 { 0199 QTest::newRow(tag) << data << cls; 0200 } 0201 0202 void ScriptClassesTest::testScriptValueContents_data() 0203 { 0204 QTest::addColumn<DataInformation*>("data"); 0205 QTest::addColumn<QScriptClass*>("scriptClass"); 0206 0207 scriptValueContentsAddRow("struct", structData, 0208 structDataTop->scriptHandler()->handlerInfo()->mStructUnionClass.data()); 0209 scriptValueContentsAddRow("union", unionData, 0210 unionDataTop->scriptHandler()->handlerInfo()->mStructUnionClass.data()); 0211 scriptValueContentsAddRow("array", arrayData, 0212 arrayDataTop->scriptHandler()->handlerInfo()->mArrayClass.data()); 0213 scriptValueContentsAddRow("string", stringData, 0214 stringDataTop->scriptHandler()->handlerInfo()->mStringClass.data()); 0215 } 0216 0217 void ScriptClassesTest::testScriptValueContents() 0218 { 0219 QFETCH(DataInformation*, data); 0220 QFETCH(QScriptClass*, scriptClass); 0221 0222 QScriptValue val = data->toScriptValue(data->topLevelDataInformation()); 0223 QVERIFY(val.isValid()); 0224 QVERIFY(val.isObject()); 0225 QCOMPARE(val.scriptClass(), scriptClass); 0226 QVERIFY(val.data().isVariant()); 0227 QVariant variant = val.data().toVariant(); 0228 QVERIFY(variant.isValid()); 0229 QVERIFY(variant.canConvert<SafeReference>()); 0230 QCOMPARE(variant.value<SafeReference>().data(), data); 0231 QCOMPARE(DefaultScriptClass::toDataInformation(val), data); 0232 } 0233 0234 void ScriptClassesTest::checkProperties(const QVector<PropertyPair>& expected, 0235 DataInformation* data, const char* tag) 0236 { 0237 // check in updating mode 0238 // TODO check also in other modes 0239 data->topLevelDataInformation()->scriptHandler()->handlerInfo()->setMode(ScriptHandlerInfo::Mode::Updating); 0240 QScriptValue value = data->toScriptValue(data->topLevelDataInformation()->scriptEngine(), 0241 data->topLevelDataInformation()->scriptHandler()->handlerInfo()); 0242 0243 QScriptValueIterator it(value); 0244 QList<PropertyPair> foundProperties; 0245 while (it.hasNext()) { 0246 it.next(); 0247 foundProperties.append(qMakePair(it.name(), it.flags())); 0248 } 0249 data->topLevelDataInformation()->scriptHandler()->handlerInfo()->setMode(ScriptHandlerInfo::Mode::None); 0250 std::sort(foundProperties.begin(), foundProperties.end()); 0251 if (foundProperties.size() != expected.size()) { 0252 for (int i = 0; i < qMin(foundProperties.size(), expected.size()); ++i) { 0253 if (foundProperties.at(i) != expected.at(i)) { 0254 qWarning() << tag << ":" << foundProperties.at(i) << ", but expected:" << expected.at(i); 0255 } 0256 QCOMPARE(foundProperties.at(i).first, expected.at(i).first); 0257 QCOMPARE(foundProperties.at(i).second, expected.at(i).second); 0258 } 0259 } 0260 for (int i = 0; i < foundProperties.size(); ++i) { 0261 if (foundProperties.at(i) != expected.at(i)) { 0262 qWarning() << tag << ":" << foundProperties.at(i) << "!=" << expected.at(i); 0263 } 0264 QCOMPARE(foundProperties.at(i).first, expected.at(i).first); 0265 QCOMPARE(foundProperties.at(i).second, expected.at(i).second); 0266 } 0267 0268 QCOMPARE(foundProperties.size(), expected.size()); 0269 } 0270 0271 void ScriptClassesTest::checkIterators() 0272 { 0273 for (auto* top : qAsConst(primitives)) { 0274 checkProperties(primitiveProperties, top->actualDataInformation(), "primitives"); 0275 } 0276 0277 checkProperties(enumProperties, enumData, "enum"); 0278 checkProperties(enumProperties, flagData, "flag"); 0279 0280 checkProperties(bitfieldProperties, boolBitfield, "bool bitfield"); 0281 checkProperties(bitfieldProperties, signedBitfield, "signed bitfield"); 0282 checkProperties(bitfieldProperties, unsignedBitfield, "unsignedBitfield"); 0283 0284 checkProperties(stringProperties, stringData, "string"); 0285 0286 checkProperties(arrayProperties, arrayData, "array"); 0287 0288 checkProperties(structUnionProperties, structData, "struct"); 0289 checkProperties(structUnionProperties, unionData, "union"); 0290 } 0291 0292 void ScriptClassesTest::testReplaceObject() 0293 { 0294 QScriptEngine* eng = ScriptEngineInitializer::newEngine(); 0295 auto* logger = new ScriptLogger(); 0296 logger->setLogToStdOut(true); 0297 QString unionDef = QStringLiteral( 0298 "union({\n" 0299 QT_UNICODE_LITERAL(" innerStruct : struct({ first : uint8(), second : uint16() }),\n") 0300 QT_UNICODE_LITERAL(" innerArray : array(uint8(), 5),\n") 0301 QT_UNICODE_LITERAL(" innerPointer : pointer(uint8(), double())\n") 0302 QT_UNICODE_LITERAL("});\n")); 0303 QScriptValue val = eng->evaluate(unionDef); 0304 QVERIFY(val.isObject()); 0305 DataInformation* main = ScriptValueConverter::convert(val, QStringLiteral("container"), logger, nullptr); 0306 QVERIFY(main); 0307 QCOMPARE(logger->rowCount(), 0); 0308 TopLevelDataInformation top(main, logger, eng); 0309 0310 // first we read the struct, which changes the type of the first child 0311 // access it again after changing to ensure it was set properly 0312 QScriptValue structUpdate = eng->evaluate(QStringLiteral( 0313 "(function() { this.first.datatype = int32(); this.first.name = \"changed\"; })")); 0314 QVERIFY(structUpdate.isFunction()); 0315 StructureDataInformation* structData = main->childAt(0)->asStruct(); 0316 QVERIFY(structData); 0317 structData->setUpdateFunc(structUpdate); 0318 QCOMPARE(structData->name(), QStringLiteral("innerStruct")); 0319 0320 // array changes its own type, this is the critical one 0321 // access it again after changing to ensure it was set properly 0322 QScriptValue arrayUpdate = eng->evaluate(QStringLiteral( 0323 "(function() { this.datatype = float(); this.name = \"changedToFloat\"; })")); 0324 ArrayDataInformation* arrayData = main->childAt(1)->asArray(); 0325 arrayData->setUpdateFunc(arrayUpdate); 0326 0327 QVERIFY(arrayData); 0328 QScriptValue pointerTargetUpdate = eng->evaluate(QStringLiteral( 0329 "(function() { this.datatype = array(int8(), 5); this.parent.name = \"changedToArrayPointer\"; })")); 0330 PointerDataInformation* ptrData = main->childAt(2)->asPointer(); 0331 QVERIFY(ptrData); 0332 ptrData->pointerTarget()->setUpdateFunc(pointerTargetUpdate); 0333 0334 QScriptValue unionUpdate = eng->evaluate(QStringLiteral( 0335 "(function() { this.datatype = ") + unionDef + QStringLiteral(" this.name = \"newContainer\"; })")); 0336 main->setUpdateFunc(unionUpdate); 0337 0338 // now just call update 0339 QCOMPARE(structData->childCount(), 2u); 0340 QCOMPARE((int)structData->childAt(0)->asPrimitive()->type(), (int)PrimitiveDataType::UInt8); 0341 QCOMPARE(structData->childAt(0)->name(), QStringLiteral("first")); 0342 QCOMPARE(structData->childAt(1)->name(), QStringLiteral("second")); 0343 top.scriptHandler()->updateDataInformation(structData); 0344 // now structdata should have different children 0345 QCOMPARE(structData->childCount(), 2u); 0346 QCOMPARE((int)structData->childAt(0)->asPrimitive()->type(), (int)PrimitiveDataType::Int32); // different now 0347 QCOMPARE(structData->childAt(0)->name(), QStringLiteral("changed")); // different now 0348 QCOMPARE(structData->childAt(1)->name(), QStringLiteral("second")); // still the same 0349 0350 QCOMPARE(arrayData->name(), QStringLiteral("innerArray")); 0351 top.scriptHandler()->updateDataInformation(arrayData); 0352 QVERIFY(main->childAt(1)->hasBeenUpdated()); 0353 QVERIFY(main->childAt(1)->isPrimitive()); 0354 QCOMPARE(main->childAt(1)->name(), QStringLiteral("changedToFloat")); 0355 0356 QCOMPARE(ptrData->name(), QStringLiteral("innerPointer")); 0357 QVERIFY(main->childAt(2)->isPointer()); 0358 QVERIFY(main->childAt(2)->asPointer()->pointerTarget()->isPrimitive()); 0359 top.scriptHandler()->updateDataInformation(ptrData->pointerTarget()); 0360 QVERIFY(main->childAt(2)->isPointer()); 0361 QVERIFY(main->childAt(2)->asPointer()->pointerTarget()->isArray()); 0362 QCOMPARE(main->childAt(2)->name(), QStringLiteral("changedToArrayPointer")); 0363 0364 // now reset to state before 0365 QCOMPARE(main->name(), QStringLiteral("container")); 0366 top.scriptHandler()->updateDataInformation(main); 0367 // main is now a dangling pointer 0368 main = top.actualDataInformation(); 0369 QString nnnname = QStringLiteral("newContainer"); 0370 QCOMPARE(main->name(), nnnname); 0371 QVERIFY(main->childAt(0)->isStruct()); 0372 QCOMPARE(main->childAt(0)->name(), QStringLiteral("innerStruct")); 0373 QCOMPARE(main->childAt(1)->name(), QStringLiteral("innerArray")); 0374 QCOMPARE(main->childAt(2)->name(), QStringLiteral("innerPointer")); 0375 } 0376 0377 namespace { 0378 QString invalidObjectError() { return QStringLiteral("ReferenceError: Attempting to access an invalid object"); } 0379 } 0380 0381 void ScriptClassesTest::testSafePrimitiveArrayReference() 0382 { 0383 QVERIFY(arrayData->arrayType()->isPrimitive()); 0384 QVERIFY(arrayData->length() > 2); 0385 arrayDataTop->logger()->setLogToStdOut(true); 0386 QScriptEngine* eng = arrayDataTop->scriptEngine(); 0387 eng->pushContext(); 0388 eng->currentContext()->activationObject().setProperty(QStringLiteral("myArray"), 0389 arrayData->toScriptValue(arrayDataTop.data())); 0390 arrayDataTop->scriptHandler()->handlerInfo()->setMode(ScriptHandlerInfo::Mode::Updating); 0391 QScriptValue v0 = eng->evaluate(QStringLiteral("myArray[0]")); 0392 QCOMPARE(Utils::property(v0, "name").toString(), QString::number(0)); 0393 QVERIFY(DefaultScriptClass::toDataInformation(v0) != nullptr); 0394 // access index 1 -> index 0 should become invalid, since there is only one object available 0395 QScriptValue v1 = eng->evaluate(QStringLiteral("myArray[1]")); 0396 QVERIFY(DefaultScriptClass::toDataInformation(v1) != nullptr); 0397 QVERIFY(DefaultScriptClass::toDataInformation(v0) == nullptr); 0398 QVERIFY(!eng->hasUncaughtException()); 0399 QCOMPARE(Utils::property(v1, "name").toString(), QString::number(1)); 0400 QVERIFY(!eng->hasUncaughtException()); 0401 QCOMPARE(Utils::property(v0, "name").toString(), invalidObjectError()); 0402 QVERIFY(!eng->hasUncaughtException()); 0403 // even after accessing a v0 property (which will fail), v1 properties should remain valid 0404 QCOMPARE(Utils::property(v1, "name").toString(), QString::number(1)); 0405 QVERIFY(!eng->hasUncaughtException()); 0406 arrayDataTop->scriptHandler()->handlerInfo()->setMode(ScriptHandlerInfo::Mode::None); 0407 eng->popContext(); 0408 } 0409 0410 void ScriptClassesTest::testSafeReferenceDeleteObject() 0411 { 0412 QScopedPointer<TopLevelDataInformation> top(Utils::evalAndParse("struct({bar: uint8()}).set({name: 'foo'});")); 0413 QVERIFY(top->actualDataInformation()->isStruct()); 0414 top->scriptHandler()->handlerInfo()->setMode(ScriptHandlerInfo::Mode::TaggedUnionSelection); 0415 QScriptValue val = top->actualDataInformation()->toScriptValue(top.data()); 0416 QScriptValue name = Utils::property(val, "name"); 0417 QVERIFY(name.isValid()); 0418 QVERIFY(!name.isError()); 0419 QCOMPARE(name.toString(), QStringLiteral("foo")); 0420 top->setActualDataInformation(new DummyDataInformation(nullptr)); 0421 // val should now point to an invalid reference -> accessing name should throw an error 0422 name = Utils::property(val, "name"); 0423 QVERIFY(name.isValid()); 0424 QVERIFY(name.isError()); 0425 QCOMPARE(name.toString(), invalidObjectError()); 0426 } 0427 0428 void ScriptClassesTest::cleanupTestCase() 0429 { 0430 qDeleteAll(primitives); 0431 } 0432 0433 QTEST_GUILESS_MAIN(ScriptClassesTest) 0434 0435 #include "scriptclassestest.moc"