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"