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&lt;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"