File indexing completed on 2024-05-05 16:15:13
0001 /* 0002 * This file is part of the KDE libraries 0003 * Copyright (C) 2008 Harri Porten (porten@kde.org) 0004 * 0005 * This library is free software; you can redistribute it and/or 0006 * modify it under the terms of the GNU Library General Public 0007 * License as published by the Free Software Foundation; either 0008 * version 2 of the License, or (at your option) any later version. 0009 * 0010 * This library is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 * Library General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU Library General Public License 0016 * along with this library; see the file COPYING.LIB. If not, write to 0017 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0018 * Boston, MA 02110-1301, USA. 0019 * 0020 */ 0021 0022 #include "kjsobject.h" 0023 #include "kjsprototype.h" 0024 #include "kjsarguments.h" 0025 #include "kjsinterpreter.h" 0026 0027 #include "qtest.h" 0028 0029 #include <QRegularExpression> 0030 0031 class KJSApiTest : public QObject 0032 { 0033 Q_OBJECT 0034 0035 private Q_SLOTS: 0036 void objectConstruction(); 0037 void interpreterEvaluate(); 0038 void interpreterNormalizeCode(); 0039 void objectProperties(); 0040 void prototypeConstants(); 0041 void prototypeProperties(); 0042 void prototypeFunctions(); 0043 void globalObject(); 0044 }; 0045 0046 void KJSApiTest::objectConstruction() 0047 { 0048 KJSInterpreter ip; 0049 KJSContext *ctx = ip.globalContext(); 0050 0051 // Object 0052 QVERIFY2(KJSObject().isObject(), "Broken default object"); 0053 0054 // undefined 0055 QVERIFY2(KJSUndefined().isUndefined(), 0056 "Undefined object is not undefined"); 0057 // null 0058 QVERIFY2(KJSNull().isNull(), 0059 "Null object is not null"); 0060 0061 // Boolean 0062 KJSBoolean boolObject(true); 0063 QVERIFY2(boolObject.isBoolean(), "Boolean object is not of boolean type"); 0064 QVERIFY2(boolObject.toBoolean(ctx), "Boolean object has wrong value"); 0065 QVERIFY2(!ctx->hasException(), "Boolean conversion threw exception"); 0066 0067 // Number 0068 KJSNumber numObject(42.0); 0069 QVERIFY2(numObject.isNumber(), "Number object is not of number type"); 0070 QCOMPARE(numObject.toNumber(ctx), 42.0); 0071 QCOMPARE(numObject.toInt32(ctx), 42); 0072 QVERIFY2(!ctx->hasException(), "Number conversion threw exception"); 0073 0074 // String 0075 KJSString stringObject("Trunk"); 0076 QVERIFY2(stringObject.isString(), "String object is not of string type"); 0077 QCOMPARE(stringObject.toString(ctx), QLatin1String("Trunk")); 0078 QVERIFY2(!ctx->hasException(), "String conversion threw exception"); 0079 0080 // Array 0081 KJSArray arrayObject(ctx, 3); 0082 QVERIFY2(arrayObject.isObject(), "Array object is not of object type"); 0083 QCOMPARE(arrayObject.property(ctx, "length").toNumber(ctx), 3.0); 0084 QCOMPARE(arrayObject.toString(ctx), QLatin1String(",,")); 0085 QVERIFY2(!ctx->hasException(), "Array conversion threw exception"); 0086 0087 // copying 0088 KJSObject copy(stringObject); 0089 QCOMPARE(copy.toString(ctx), QLatin1String("Trunk")); 0090 copy = numObject; 0091 QCOMPARE(copy.toNumber(ctx), 42.0); 0092 } 0093 0094 void KJSApiTest::interpreterEvaluate() 0095 { 0096 KJSInterpreter ip; 0097 KJSContext *ctx = ip.globalContext(); 0098 KJSResult res; 0099 0100 // syntax error 0101 res = ip.evaluate(")("); 0102 QVERIFY2(res.isException(), "Syntax error not caught"); 0103 0104 res = ip.evaluate("11+22"); 0105 QVERIFY2(!res.isException(), "Evaluation returned non-number object"); 0106 QCOMPARE(res.value().toNumber(ctx), 33.0); 0107 } 0108 0109 void KJSApiTest::interpreterNormalizeCode() 0110 { 0111 int errLine = -1; 0112 QString errMsg; 0113 QString norm; 0114 bool ok; 0115 0116 // syntax error case 0117 ok = KJSInterpreter::normalizeCode(")(", &norm, &errLine, &errMsg); 0118 QVERIFY(!ok); 0119 QVERIFY(!errMsg.isEmpty()); 0120 QVERIFY(errLine >= 0 && errLine <= 2); // ### imprecise 0121 0122 // success case 0123 ok = KJSInterpreter::normalizeCode("foo(); bar();", &norm); 0124 QVERIFY(ok); 0125 QVERIFY(!norm.isEmpty()); 0126 QStringList lines = norm.split('\n'); 0127 QVERIFY(lines.size() >= 2); // ### imprecise 0128 int fooLine = lines.indexOf(QRegularExpression(QStringLiteral(" *foo\\(\\);"))); 0129 int barLine = lines.indexOf(QRegularExpression(QStringLiteral(" *bar\\(\\);"))); 0130 QVERIFY(fooLine >= 0); 0131 QVERIFY(barLine > fooLine); 0132 } 0133 0134 void KJSApiTest::objectProperties() 0135 { 0136 KJSInterpreter ip; 0137 KJSContext *ctx = ip.globalContext(); 0138 0139 KJSObject global = ip.globalObject(); 0140 KJSObject v; 0141 0142 // bool 0143 global.setProperty(ctx, "myprop", true); 0144 v = global.property(ctx, "myprop"); 0145 QVERIFY(v.isBoolean()); 0146 QCOMPARE(v.toBoolean(ctx), true); 0147 0148 // double 0149 global.setProperty(ctx, "myprop", 21.0); 0150 v = global.property(ctx, "myprop"); 0151 QVERIFY(v.isNumber()); 0152 QCOMPARE(v.toNumber(ctx), 21.0); 0153 0154 // int 0155 global.setProperty(ctx, "myprop", 22); 0156 v = global.property(ctx, "myprop"); 0157 QVERIFY(v.isNumber()); 0158 QCOMPARE(v.toNumber(ctx), 22.0); 0159 0160 // string (8-bit) 0161 global.setProperty(ctx, "myprop", "myvalue8"); 0162 v = global.property(ctx, "myprop"); 0163 QVERIFY(v.isString()); 0164 QCOMPARE(v.toString(ctx), QLatin1String("myvalue8")); 0165 0166 // string (Unicode) 0167 global.setProperty(ctx, "myprop", QLatin1String("myvalue16")); 0168 v = global.property(ctx, "myprop"); 0169 QVERIFY(v.isString()); 0170 QCOMPARE(v.toString(ctx), QLatin1String("myvalue16")); 0171 } 0172 0173 void KJSApiTest::prototypeConstants() 0174 { 0175 KJSInterpreter ip; 0176 KJSContext *ctx = ip.globalContext(); 0177 0178 KJSPrototype proto; 0179 0180 proto.defineConstant("d0", 44.4); 0181 proto.defineConstant("s0", QLatin1String("XYZ")); 0182 0183 KJSObject obj = proto.constructObject(ctx, nullptr); 0184 0185 QCOMPARE(obj.property(ctx, "d0").toNumber(ctx), 44.4); 0186 QCOMPARE(obj.property(ctx, "s0").toString(ctx), QLatin1String("XYZ")); 0187 } 0188 0189 static struct O { 0190 int x; 0191 } o0 = { 42 }; 0192 0193 static KJSObject getX(KJSContext * /*context*/, void *object) 0194 { 0195 O *o = reinterpret_cast<O *>(object); 0196 int x = o->x; 0197 return KJSNumber(x); 0198 } 0199 0200 static void setX(KJSContext *context, void *object, KJSObject value) 0201 { 0202 O *o = reinterpret_cast<O *>(object); 0203 double n = value.toNumber(context); 0204 o->x = n; 0205 } 0206 0207 void KJSApiTest::prototypeProperties() 0208 { 0209 KJSInterpreter ip; 0210 KJSContext *ctx = ip.globalContext(); 0211 0212 KJSPrototype proto; 0213 0214 proto.defineProperty(ctx, "x", getX, setX); 0215 proto.defineProperty(ctx, "readOnlyX", getX); 0216 0217 KJSObject obj = proto.constructObject(ctx, &o0); 0218 0219 // read/write property 0220 QCOMPARE(obj.property(ctx, "x").toNumber(ctx), 42.0); 0221 obj.setProperty(ctx, "x", KJSNumber(43)); 0222 QCOMPARE(obj.property(ctx, "x").toNumber(ctx), 43.0); 0223 0224 QCOMPARE(obj.property(ctx, "readOnlyX").toNumber(ctx), 43.0); 0225 obj.setProperty(ctx, "readOnlyX", KJSNumber(44)); 0226 QVERIFY2(ctx->hasException(), "Write access caused no exception"); 0227 QCOMPARE(obj.property(ctx, "readOnlyX").toNumber(ctx), 43.0); 0228 } 0229 0230 static KJSObject multiply(KJSContext *context, void *object, 0231 const KJSArguments &arguments) 0232 { 0233 double factor = *reinterpret_cast<double *>(object); 0234 0235 // test number of arguments 0236 if (arguments.count() != 1) { 0237 return context->throwException("Missing argument"); 0238 } 0239 0240 KJSObject a0 = arguments.at(0); 0241 if (!a0.isNumber()) { 0242 return KJSNumber(-2); 0243 } 0244 0245 double v0 = a0.toNumber(context); 0246 0247 return KJSNumber(factor * v0); 0248 } 0249 0250 void KJSApiTest::prototypeFunctions() 0251 { 0252 KJSInterpreter ip; 0253 KJSContext *ctx = ip.globalContext(); 0254 0255 KJSPrototype proto; 0256 0257 proto.defineFunction(ctx, "multiply", multiply); 0258 0259 double factor = 3.0; 0260 KJSObject obj = proto.constructObject(ctx, &factor); 0261 ip.globalObject().setProperty(ctx, "obj", obj); 0262 0263 KJSResult res = ip.evaluate("obj.multiply(4)"); 0264 QCOMPARE(res.value().toNumber(ctx), 12.0); 0265 0266 // expect exception 0267 res = ip.evaluate("obj.multiply()"); 0268 QVERIFY2(res.isException(), "Exception did not work"); 0269 } 0270 0271 void KJSApiTest::globalObject() 0272 { 0273 KJSPrototype proto; 0274 proto.defineConstant("g0", 55.5); 0275 0276 KJSGlobalObject glob = proto.constructGlobalObject(nullptr); 0277 0278 KJSInterpreter ip(glob); 0279 KJSResult res = ip.evaluate("2 * g0"); 0280 QCOMPARE(res.value().toNumber(ip.globalContext()), 111.0); 0281 } 0282 0283 QTEST_MAIN(KJSApiTest) 0284 0285 #include "kjsapitest.moc"