File indexing completed on 2024-05-12 15:19:52
0001 /************************************************************************************* 0002 * Copyright (C) 2007 by Aleix Pol <aleixpol@kde.org> * 0003 * * 0004 * This program is free software; you can redistribute it and/or * 0005 * modify it under the terms of the GNU General Public License * 0006 * as published by the Free Software Foundation; either version 2 * 0007 * of the License, or (at your option) any later version. * 0008 * * 0009 * This program is distributed in the hope that it will be useful, * 0010 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0012 * GNU General Public License for more details. * 0013 * * 0014 * You should have received a copy of the GNU General Public License * 0015 * along with this program; if not, write to the Free Software * 0016 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * 0017 *************************************************************************************/ 0018 0019 #include "exptest.h" 0020 #include "explexer.h" 0021 #include "expressionparser.h" 0022 #include <QTest> 0023 #include <QDebug> 0024 0025 QTEST_MAIN( ExpTest ) 0026 ExpTest::ExpTest(QObject *parent) 0027 : QObject(parent) 0028 {} 0029 0030 ExpTest::~ExpTest() 0031 {} 0032 0033 Q_DECLARE_METATYPE(QList<uchar>) 0034 0035 void ExpTest::initTestCase() {} 0036 0037 void ExpTest::cleanupTestCase() {} 0038 0039 void ExpTest::testSimple_data() 0040 { 0041 QTest::addColumn<QString>("input"); 0042 QTest::addColumn<QString>("output"); 0043 0044 QTest::newRow("1 value") << "1" << "<math><cn>1</cn></math>"; 0045 QTest::newRow("1.5 value") << "1.5" << "<math><cn type='real'>1.5</cn></math>"; 0046 QTest::newRow(".5 value") << ".5" << "<math><cn type='real'>.5</cn></math>"; 0047 // QTest::newRow("1. value") << "1." << "<math><cn type='real'>1.</cn></math>"; 0048 QTest::newRow("exp_vals") << "1e1+1e-2+-1e3" << "<math><apply><plus /><apply><plus /><cn type='real'>1e1</cn><cn type='real'>1e-2</cn></apply><apply><minus /><cn type='real'>1e3</cn></apply></apply></math>"; 0049 0050 QTest::newRow("addition") << "1+2" << "<math><apply><plus /><cn>1</cn><cn>2</cn></apply></math>"; 0051 QTest::newRow("equality") << "1=2" << "<math><apply><eq /><cn>1</cn><cn>2</cn></apply></math>"; 0052 QTest::newRow("subtraction") << "2-3" << "<math><apply><minus /><cn>2</cn><cn>3</cn></apply></math>"; 0053 QTest::newRow("unary minus alone") << "-3" << "<math><apply><minus /><cn>3</cn></apply></math>"; 0054 QTest::newRow("x*unary minus") << "x*(-3)" << "<math><apply><times /><ci>x</ci>" 0055 "<apply><minus /><cn>3</cn></apply></apply></math>"; 0056 QTest::newRow("unary minus*x") << "-3*x" << "<math><apply><times /><apply><minus />" 0057 "<cn>3</cn></apply><ci>x</ci></apply></math>"; 0058 0059 QTest::newRow("assignation") << "x:=2+3" << "<math><declare><ci>x</ci><apply><plus /><cn>2</cn>" 0060 "<cn>3</cn></apply></declare></math>"; 0061 QTest::newRow("simple expression") << "2+1-3" << "<math><apply><minus /><apply><plus />" 0062 "<cn>2</cn><cn>1</cn></apply><cn>3</cn></apply></math>"; 0063 QTest::newRow("times") << "1*2" << "<math><apply><times /><cn>1</cn><cn>2</cn></apply></math>"; 0064 QTest::newRow("power") << "1^2" << "<math><apply><power /><cn>1</cn><cn>2</cn></apply></math>"; 0065 QTest::newRow("power") << "1**2"<< "<math><apply><power /><cn>1</cn><cn>2</cn></apply></math>"; 0066 QTest::newRow("times") << "1/2" << "<math><apply><divide /><cn>1</cn><cn>2</cn></apply></math>"; 0067 QTest::newRow("priority") << "2+3*5" << "<math><apply><plus /><cn>2</cn><apply><times />" 0068 "<cn>3</cn><cn>5</cn></apply></apply></math>"; 0069 QTest::newRow("unicode y*x**2") << "y*x²" << "<math><apply><times /><ci>y</ci><apply><power /><ci>x</ci><cn>2</cn></apply></apply></math>"; //x**2y 0070 QTest::newRow("unicode x**2*y") << "x²*y" << "<math><apply><times /><apply><power /><ci>x</ci><cn>2</cn></apply><ci>y</ci></apply></math>"; //x**2y 0071 0072 QTest::newRow("function") << "func(x, y)" << "<math><apply><ci type='function'>func</ci>" 0073 "<ci>x</ci><ci>y</ci></apply></math>"; 0074 QTest::newRow("function_np") << "sin x/x" << "<math><apply><divide /><apply><sin /><ci>x</ci></apply><ci>x</ci></apply></math>"; 0075 QTest::newRow("block") << "blk{x, y}" << "<math><blk><ci>x</ci><ci>y</ci></blk></math>"; 0076 0077 QTest::newRow("sum") << "sum(x : x=1..10)" << "<math>" 0078 "<apply><sum /><bvar><ci>x</ci></bvar><uplimit><cn>10</cn></uplimit><downlimit>" 0079 "<cn>1</cn></downlimit><ci>x</ci></apply></math>"; 0080 QTest::newRow("x*sum") << "x*sum(x : x=1..10)" << 0081 "<math><apply><times /><ci>x</ci><apply><sum /><bvar><ci>x</ci></bvar>" 0082 "<uplimit><cn>10</cn></uplimit><downlimit><cn>1</cn></downlimit><ci>x</ci></apply></apply></math>"; 0083 QTest::newRow("piecewise") << "piecewise{x?a, ?b}" << "<math><piecewise><piece><ci>a</ci><ci>x</ci></piece>" 0084 "<otherwise><ci>b</ci></otherwise></piecewise></math>"; 0085 QTest::newRow("piecewise2") << "piecewise{?b}" << "<math><piecewise><otherwise><ci>b</ci></otherwise></piecewise></math>"; 0086 0087 QTest::newRow("piecewise2") << "fib:=n->piecewise { eq(n,0)?0, eq(n,1)?1, ?fib(n-1)+fib(n-2) }" << "<math><declare><ci>fib</ci><lambda><bvar><ci>n</ci></bvar><piecewise><piece><cn>0</cn><apply><eq /><ci>n</ci><cn>0</cn></apply></piece><piece><cn>1</cn><apply><eq /><ci>n</ci><cn>1</cn></apply></piece><otherwise><apply><plus /><apply><ci type='function'>fib</ci><apply><minus /><ci>n</ci><cn>1</cn></apply></apply><apply><ci type='function'>fib</ci><apply><minus /><ci>n</ci><cn>2</cn></apply></apply></apply></otherwise></piecewise></lambda></declare></math>"; 0088 0089 QTest::newRow("lambda") << "f:=x->(3)" << "<math><declare><ci>f</ci><lambda><bvar><ci>x</ci></bvar><cn>3</cn></lambda></declare></math>"; 0090 QTest::newRow("lambda2") << "f:=(x, y)->(x+y)" 0091 << "<math><declare><ci>f</ci><lambda><bvar><ci>x</ci></bvar><bvar><ci>y</ci></bvar><apply><plus /><ci>x</ci><ci>y</ci></apply></lambda></declare></math>"; 0092 QTest::newRow("lambda3") << "g:=y->y" << "<math><declare><ci>g</ci><lambda><bvar><ci>y</ci></bvar><ci>y</ci></lambda></declare></math>"; 0093 QTest::newRow("unary minus") << "1*(-2)" << "<math><apply><times /><cn>1</cn><apply>" 0094 "<minus /><cn>2</cn></apply></apply></math>"; 0095 QTest::newRow("bounds and !limit") << "func(x:x)" << "<math><apply><ci type='function'>func</ci>" 0096 "<bvar><ci>x</ci></bvar><ci>x</ci></apply></math>"; 0097 QTest::newRow("bounds and limit") << "func(x+y : x=0..1)" << 0098 "<math><apply><ci type='function'>func</ci>" 0099 "<bvar><ci>x</ci></bvar><uplimit><cn>1</cn></uplimit><downlimit><cn>0</cn></downlimit>" 0100 "<apply><plus /><ci>x</ci><ci>y</ci></apply></apply></math>"; 0101 QTest::newRow("bounds and limit") << "card(vector { x, y, z })" 0102 << "<math><apply><card /><vector><ci>x</ci><ci>y</ci><ci>z</ci></vector></apply></math>"; 0103 QTest::newRow("lambda call") << "(x->x+2)(2)" << "<math><apply><lambda><bvar><ci>x</ci></bvar><apply><plus /><ci>x</ci><cn>2</cn></apply></lambda><cn>2</cn></apply></math>"; 0104 QTest::newRow("union") << "union(list{1}, list {2})" << "<math><apply><union /><list><cn>1</cn></list><list><cn>2</cn></list></apply></math>"; 0105 QTest::newRow("list") << "list{}" << "<math><list /></math>"; 0106 QTest::newRow("minus order") << "-3^2" << "<math><apply><minus /><apply><power /><cn>3</cn><cn>2</cn></apply></apply></math>"; 0107 QTest::newRow("minus order") << "v[3]" << "<math><apply><selector /><cn>3</cn><ci>v</ci></apply></math>"; 0108 0109 QTest::newRow("comment1") << "//a\nv[3]" << "<math><apply><selector /><cn>3</cn><ci>v</ci></apply></math>"; 0110 QTest::newRow("comment2") << "//a//v[3]" << "<math><apply><selector /><cn>3</cn><ci>v</ci></apply></math>"; 0111 QTest::newRow("comment3") << "v//a//[3]" << "<math><apply><selector /><cn>3</cn><ci>v</ci></apply></math>"; 0112 QTest::newRow("comment4") << "v[3]//a//" << "<math><apply><selector /><cn>3</cn><ci>v</ci></apply></math>"; 0113 QTest::newRow("comment5") << "v[3]//a\n" << "<math><apply><selector /><cn>3</cn><ci>v</ci></apply></math>"; 0114 0115 QTest::newRow("string1") << "\"hola\"" << "<math><cs>hola</cs></math>"; 0116 QTest::newRow("string2") << "\"a<b\"" << "<math><cs>a<b</cs></math>"; 0117 // QTest::newRow("string3") << "\"a\\nb\"" << "<math><cs>a\nb</cs></math>"; 0118 0119 QTest::newRow("pipe1") << "3 | sin" << "<math><apply><sin /><cn>3</cn></apply></math>"; 0120 QTest::newRow("pipe2") << "3|sin|cos|tan" << "<math><apply><tan /><apply><cos /><apply><sin /><cn>3</cn></apply></apply></apply></math>"; 0121 QTest::newRow("pipe3") << "3|f|g" << "<math><apply><ci type='function'>g</ci><apply><ci type='function'>f</ci><cn>3</cn></apply></apply></math>"; 0122 QTest::newRow("pipe4") << "3| (x->f(3,x)) |g" << "<math><apply><ci type='function'>g</ci><apply><lambda><bvar><ci>x</ci></bvar><apply><ci type='function'>f</ci><cn>3</cn><ci>x</ci></apply></lambda><cn>3</cn></apply></apply></math>"; 0123 } 0124 0125 void ExpTest::testSimple() 0126 { 0127 QFETCH(QString, input); 0128 QFETCH(QString, output); 0129 0130 ExpLexer lex(input); 0131 ExpressionParser parser; 0132 bool corr=parser.parse(&lex); 0133 0134 if(!parser.error().isEmpty()) 0135 qDebug() << ">>> " << parser.mathML() << "errors:" << parser.error(); 0136 QVERIFY(corr); 0137 QVERIFY(parser.error().isEmpty()); 0138 QCOMPARE(parser.mathML(), output); 0139 } 0140 0141 void ExpTest::testExp_data() 0142 { 0143 QTest::addColumn<QString>("input"); 0144 QTest::addColumn<QString>("output"); 0145 0146 QString fourX=QStringLiteral("<math><apply><plus /><ci>x</ci><ci>x</ci><ci>x</ci><ci>x</ci></apply></math>"); 0147 0148 //FIXME: Repetition not supported 0149 // QTest::newRow("simple expression") << "x+x+x+x" << fourX; 0150 QTest::newRow("composed expression") << QStringLiteral("3²") 0151 << "<math><apply><power /><cn>3</cn><cn>2</cn></apply></math>"; 0152 QTest::newRow("plus operator in plus() form") << "plus(x,x,x,x)" << fourX; 0153 QTest::newRow("sum") << "x*sum(x : x=1..10)" << "<math><apply><times /><ci>x</ci>" 0154 "<apply><sum /><bvar><ci>x</ci></bvar><uplimit><cn>10</cn></uplimit><downlimit>" 0155 "<cn>1</cn></downlimit><ci>x</ci></apply></apply></math>"; 0156 0157 QTest::newRow("lol") << "times((x),(y))" << "<math><apply><times /><ci>x</ci><ci>y</ci></apply></math>"; 0158 QTest::newRow("lol1") << "times((x),(y),((z)))" << "<math><apply><times /><ci>x</ci><ci>y</ci><ci>z</ci></apply></math>"; 0159 QTest::newRow("notimes") << "2x" << "<math><apply><times /><cn>2</cn><ci>x</ci></apply></math>"; 0160 // QTest::newRow("notimes_sin") << "2(sin x)" << 0161 // "<math><apply><times /><cn>2</cn><apply><sin /><ci>x</ci></apply></apply></math>"; 0162 // QTest::newRow("notimes_sin") << "2sin(x)" << 0163 // "<math><apply><times /><cn>2</cn><apply><sin /><ci>x</ci></apply></apply></math>"; 0164 } 0165 0166 void ExpTest::testExp() 0167 { 0168 QFETCH(QString, input); 0169 QFETCH(QString, output); 0170 0171 ExpLexer lex(input); 0172 ExpressionParser parser; 0173 bool corr=parser.parse(&lex); 0174 if(!parser.error().isEmpty()) 0175 qDebug() << "errors:" << parser.error(); 0176 QVERIFY(parser.error().isEmpty() && corr); 0177 QCOMPARE(parser.mathML(), output); 0178 } 0179 0180 void ExpTest::testLength_data() 0181 { 0182 QTest::addColumn<QString>("input"); 0183 QTest::addColumn<QList<uchar> >("lengths"); 0184 0185 QList<uchar> lengths; 0186 lengths << 1 << 1 << 1 << 1 << 1; 0187 QTest::newRow("simple addition") << "2+2+2" << lengths; 0188 0189 lengths.clear(); 0190 lengths << 2 << 3; 0191 QTest::newRow("power with utf composition") << QStringLiteral("22²²²") << lengths; 0192 0193 lengths.clear(); 0194 lengths << 1; 0195 QTest::newRow("utf composed variable") << QStringLiteral("ç") << lengths; 0196 0197 lengths.clear(); 0198 lengths << 1 << 1; 0199 QTest::newRow("no times operator") << QStringLiteral("2x") << lengths; 0200 } 0201 0202 void ExpTest::testLength() 0203 { 0204 QFETCH(QString, input); 0205 QFETCH(QList<uchar>, lengths); 0206 0207 ExpLexer lex=ExpLexer(input); 0208 0209 int current=0; 0210 while(lex.lex()!=0/*EOF*/) { 0211 QVERIFY(lex.current.type>0); 0212 QCOMPARE((int) lengths[current], (int) lex.current.len); 0213 0214 current++; 0215 } 0216 } 0217 0218 void ExpTest::testCorrection_data() 0219 { 0220 QTest::addColumn<QString>("input"); 0221 QTest::addColumn<bool>("correct"); 0222 0223 QTest::newRow("stack killing") << "k+++k" << false; 0224 QTest::newRow("more stack killing") << "k-++k" << false; 0225 QTest::newRow("omartinez bug") << "piecewise { gt(x,23)?a }" << true; 0226 QTest::newRow("wrong coma") << "2+2," << false; 0227 QTest::newRow("wrong token") << "q-<" << false; 0228 QTest::newRow("misplaced declaration") << "2+2+(w:=2)" << false; 0229 } 0230 0231 void ExpTest::testCorrection() 0232 { 0233 QFETCH(QString, input); 0234 QFETCH(bool, correct); 0235 0236 ExpLexer lex(input); 0237 ExpressionParser parser; 0238 bool corr=parser.parse(&lex); 0239 QCOMPARE(corr, correct); 0240 QCOMPARE(parser.error().isEmpty(), correct); 0241 } 0242 0243 0244 0245 #include "moc_exptest.cpp"