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