diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
commit | 8362bf63dea22bbf6736609b0f49c152f975eb63 (patch) | |
tree | 0eea3928e39e50fae91d4e68b21b1e6cbae25604 /kspread/tests | |
download | koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.tar.gz koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.zip |
Added old abandoned KDE3 version of koffice
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1077364 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kspread/tests')
-rw-r--r-- | kspread/tests/Makefile.am | 33 | ||||
-rw-r--r-- | kspread/tests/README | 2 | ||||
-rw-r--r-- | kspread/tests/formula_tester.cc | 448 | ||||
-rw-r--r-- | kspread/tests/formula_tester.h | 64 | ||||
-rwxr-xr-x | kspread/tests/generate-openformula-tests | 44 | ||||
-rw-r--r-- | kspread/tests/inspector.cc | 261 | ||||
-rw-r--r-- | kspread/tests/inspector.h | 46 | ||||
-rwxr-xr-x | kspread/tests/oasis-kspread.sh | 87 | ||||
-rw-r--r-- | kspread/tests/test_formula.cc | 60 | ||||
-rw-r--r-- | kspread/tests/tester.cc | 54 | ||||
-rw-r--r-- | kspread/tests/tester.h | 50 | ||||
-rw-r--r-- | kspread/tests/testrunner.cc | 132 | ||||
-rw-r--r-- | kspread/tests/testrunner.h | 51 | ||||
-rw-r--r-- | kspread/tests/value_tester.cc | 307 | ||||
-rw-r--r-- | kspread/tests/value_tester.h | 44 |
15 files changed, 1683 insertions, 0 deletions
diff --git a/kspread/tests/Makefile.am b/kspread/tests/Makefile.am new file mode 100644 index 00000000..87c3bbdc --- /dev/null +++ b/kspread/tests/Makefile.am @@ -0,0 +1,33 @@ +KDE_CXXFLAGS = $(USE_RTTI) + +INCLUDES = \ + $(KOFFICE_INCLUDES) \ + $(KOTEXT_INCLUDES) \ + -I$(srcdir)/.. \ + $(all_includes) + +check_PROGRAMS = formulatest +TESTS = formulatest +formulatest_SOURCES = test_formula.cc +formulatest_LDADD = ../libkspreadcommon.la + + +noinst_LTLIBRARIES = \ + libtests.la + +libtests_la_SOURCES = \ + tester.cc \ + testrunner.cc \ + value_tester.cc \ + formula_tester.cc \ + inspector.cc + +noinst_HEADERS = \ + tester.h \ + testrunner.h \ + value_tester.h \ + formula_tester.h \ + inspector.h + +METASOURCES = AUTO + diff --git a/kspread/tests/README b/kspread/tests/README new file mode 100644 index 00000000..17edde26 --- /dev/null +++ b/kspread/tests/README @@ -0,0 +1,2 @@ +The tests are run inside of kspread by pressing Ctrl + Shift + T. +See ../DEBUG for more information. diff --git a/kspread/tests/formula_tester.cc b/kspread/tests/formula_tester.cc new file mode 100644 index 00000000..3dad1caa --- /dev/null +++ b/kspread/tests/formula_tester.cc @@ -0,0 +1,448 @@ +/* This file is part of the KDE project + Copyright 2004 Ariya Hidayat <ariya@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include <klocale.h> +#include <kdebug.h> + +#include "tester.h" +#include "formula_tester.h" + +#include <formula.h> +#include <kspread_util.h> +#include <kspread_value.h> + +#define CHECK_PARSE(x,y) checkParse(__FILE__,__LINE__,#x,x,y) +#define CHECK_EVAL(x,y) checkEval(__FILE__,__LINE__,#x,x,y) +#define CHECK_OASIS(x,y) checkOasis(__FILE__,__LINE__,#x,x,y) + +using namespace KSpread; + +FormulaParserTester::FormulaParserTester(): Tester() +{ +} + +QString FormulaParserTester::name() +{ + return QString("Formula (Parser)"); +} + +static char encodeTokenType( const Token& token ) +{ + char result = '?'; + switch( token.type() ) + { + case Token::Boolean: result = 'b'; break; + case Token::Integer: result = 'i'; break; + case Token::Float: result = 'f'; break; + case Token::Operator: result = 'o'; break; + case Token::Cell: result = 'c'; break; + case Token::Range: result = 'r'; break; + case Token::Identifier: result = 'x'; break; + default: break; + } + return result; +} + +static QString describeTokenCodes( const QString& tokenCodes ) +{ + QString result; + + if( tokenCodes.isEmpty() ) + result = "(invalid)"; + else + for( unsigned i = 0; i < tokenCodes.length(); i++ ) + { + switch( tokenCodes[i] ) + { + case 'b': result.append( "Boolean" ); break; + case 'i': result.append( "integer" ); break; + case 'f': result.append( "float" ); break; + case 'o': result.append( "operator" ); break; + case 'c': result.append( "cell" ); break; + case 'r': result.append( "range" ); break; + case 'x': result.append( "identifier" ); break; + default: result.append( "unknown" ); break; + } + if( i < tokenCodes.length()-1 ) result.append( ", " ); + } + + return result.prepend("{").append("}"); +} + +void FormulaParserTester::checkParse( const char *file, int line, const char* msg, + const QString& formula, const QString& tokenCodes ) +{ + testCount++; + + Formula f; + QString expr = formula; + expr.prepend( '=' ); + f.setExpression( expr ); + Tokens tokens = f.tokens(); + + QString resultCodes; + if( tokens.valid() ) + for( unsigned i = 0; i < tokens.count(); i++ ) + resultCodes.append( encodeTokenType( tokens[i] ) ); + + if( resultCodes != tokenCodes ) + { + QString message = msg; + message.append( " Result: ").append( describeTokenCodes( resultCodes ) ); + message.append( " Expected: ").append( describeTokenCodes( tokenCodes ) ); + fail( file, line, message ); + } +} + +void FormulaParserTester::run() +{ + testCount = 0; + errorList.clear(); + + // simple, single-token formulas + CHECK_PARSE( "True", "x" ); + CHECK_PARSE( "False", "x" ); + CHECK_PARSE( "36", "i" ); + CHECK_PARSE( "0", "i" ); + CHECK_PARSE( "3.14159", "f" ); + CHECK_PARSE( ".25", "f" ); + CHECK_PARSE( "1e-9", "f" ); + CHECK_PARSE( "2e3", "f" ); + CHECK_PARSE( ".3333e0", "f" ); + + // cell/range/identifier + CHECK_PARSE( "A1", "c" ); + CHECK_PARSE( "Sheet1!A1", "c" ); + CHECK_PARSE( "'Sheet1'!A1", "c" ); + CHECK_PARSE( "'Sheet One'!A1", "c" ); + CHECK_PARSE( "2006!A1", "c" ); + CHECK_PARSE( "2006bak!A1", "c" ); + CHECK_PARSE( "2006bak2!A1", "c" ); + CHECK_PARSE( "'2006bak2'!A1", "c" ); + CHECK_PARSE( "A1:B100", "r" ); + CHECK_PARSE( "Sheet1!A1:B100", "r" ); + CHECK_PARSE( "'Sheet One'!A1:B100", "r" ); + CHECK_PARSE( "SIN", "x" ); + // log2 and log10 are cell references and function identifiers + CHECK_PARSE( "LOG2", "c" ); + CHECK_PARSE( "LOG10:11", "r" ); + CHECK_PARSE( "LOG2(2)", "xoio" ); + CHECK_PARSE( "LOG10(10)", "xoio" ); + + // operators + CHECK_PARSE( "+", "o" ); + CHECK_PARSE( "-", "o" ); + CHECK_PARSE( "*", "o" ); + CHECK_PARSE( "/", "o" ); + CHECK_PARSE( "+", "o" ); + CHECK_PARSE( "^", "o" ); + CHECK_PARSE( "(", "o" ); + CHECK_PARSE( ")", "o" ); + CHECK_PARSE( ",", "o" ); + CHECK_PARSE( ";", "o" ); + CHECK_PARSE( "=", "o" ); + CHECK_PARSE( "<", "o" ); + CHECK_PARSE( ">", "o" ); + CHECK_PARSE( "<=", "o" ); + CHECK_PARSE( ">=", "o" ); + CHECK_PARSE( "%", "o" ); + + // commonly used formulas + CHECK_PARSE( "A1+A2", "coc" ); + CHECK_PARSE( "2.5*B1", "foc" ); + CHECK_PARSE( "SUM(A1:Z10)", "xoro" ); + CHECK_PARSE( "MAX(Sheet1!Sales)", "xoro" ); + CHECK_PARSE( "-ABS(A1)", "oxoco" ); + + // should be correctly parsed though they are nonsense (can't be evaluated) + CHECK_PARSE( "0E0.5", "ff" ); + CHECK_PARSE( "B3 D4:D5 Sheet1!K1", "crc" ); + CHECK_PARSE( "SIN A1", "xc" ); + CHECK_PARSE( "SIN A1:A20", "xr" ); + + // invalid formulas, can't be parsed correctly + CHECK_PARSE( "+1.23E", QString::null ); +} + +FormulaEvalTester::FormulaEvalTester(): Tester() +{ +} + +QString FormulaEvalTester::name() +{ + return QString("Formula (Eval)"); +} + +void FormulaEvalTester::checkEval( const char *file, int line, const char* msg, + const QString& formula, const Value& expected ) +{ + testCount++; + + Formula f; + QString expr = formula; + if ( expr[0] != '=' ) + expr.prepend( '=' ); + f.setExpression( expr ); + Value result = f.eval(); + + if( !result.equal( expected ) ) + { + QString message; + QTextStream ts( &message, IO_WriteOnly ); + ts << msg; + ts << " Result: " << result; + ts << " Expected: " << expected; + fail( file, line, message ); + } +} + + +void FormulaEvalTester::run() +{ + testCount = 0; + errorList.clear(); + + // simple constants + CHECK_EVAL( "0", Value(0) ); + CHECK_EVAL( "1", Value(1) ); + CHECK_EVAL( "-1", Value(-1) ); + CHECK_EVAL( "3.14e7", Value(3.14e7) ); + CHECK_EVAL( "3.14e-7", Value(3.14e-7) ); + + + // simple binary operation + CHECK_EVAL( "0+0", Value(0) ); + CHECK_EVAL( "1+1", Value(2) ); + + // unary minus + CHECK_EVAL( "-1", Value(-1) ); + CHECK_EVAL( "--1", Value(1) ); + CHECK_EVAL( "---1", Value(-1) ); + CHECK_EVAL( "----1", Value(1) ); + CHECK_EVAL( "-----1", Value(-1) ); + CHECK_EVAL( "5-1", Value(4) ); + CHECK_EVAL( "5--1", Value(6) ); + CHECK_EVAL( "5---1", Value(4) ); + CHECK_EVAL( "5----1", Value(6) ); + CHECK_EVAL( "5-----1", Value(4) ); + CHECK_EVAL( "5-----1*2.5", Value(2.5) ); + CHECK_EVAL( "5------1*2.5", Value(7.5) ); + CHECK_EVAL( "-SIN(0)", Value(0) ); + CHECK_EVAL( "1.1-SIN(0)", Value(1.1) ); + CHECK_EVAL( "1.2--SIN(0)", Value(1.2) ); + CHECK_EVAL( "1.3---SIN(0)", Value(1.3) ); + CHECK_EVAL( "-COS(0)", Value(-1) ); + CHECK_EVAL( "1.1-COS(0)", Value(0.1) ); + CHECK_EVAL( "1.2--COS(0)", Value(2.2) ); + CHECK_EVAL( "1.3---COS(0)", Value(0.3) ); + + // no parentheses, checking operator precendences + CHECK_EVAL( "14+3*77", Value(245) ); + CHECK_EVAL( "14-3*77", Value(-217) ); + CHECK_EVAL( "26*4+81", Value(185) ); + CHECK_EVAL( "26*4-81", Value(23) ); + CHECK_EVAL( "30-45/3", Value(15) ); + CHECK_EVAL( "45+45/3", Value(60) ); + CHECK_EVAL( "4+3*2-1", Value(9) ); + + // power operator is right associative + CHECK_EVAL( "2^3", Value(8) ); + CHECK_EVAL( "2^3^2", Value(512) ); + + // lead to division by zero + CHECK_EVAL( "0/0", Value::errorDIV0() ); + CHECK_EVAL( "1/0", Value::errorDIV0() ); + CHECK_EVAL( "-4/0", Value::errorDIV0() ); + CHECK_EVAL( "(2*3)/(6-2*3)", Value::errorDIV0() ); + CHECK_EVAL( "1e3+7/0", Value::errorDIV0() ); + CHECK_EVAL( "2^(99/0)", Value::errorDIV0() ); + + // string expansion ... + CHECK_EVAL( "\"2\"+5", Value(7) ); + CHECK_EVAL( "2+\"5\"", Value(7) ); + CHECK_EVAL( "\"2\"+\"5\"", Value(7) ); + + //the built-in sine function + CHECK_EVAL ("SIN(0)", Value(0)); + CHECK_EVAL ("2+sin(\"2\"-\"2\")", Value(2)); + CHECK_EVAL ("\"1\"+sin(\"0\")", Value(1)); + + // tests from the OpenFormula testing suite: + // note that these get auto-generated using generate-openformula-tests + CHECK_EVAL("=(1/3)*3=1", Value(true)); // row 51 + CHECK_EVAL("=(\"4\" & \"5\")+2", Value(47)); // row 57 + CHECK_EVAL("=2+(\"4\" & \"5\")", Value(47)); // row 58 + CHECK_EVAL("=1+2", Value(3)); // row 63 + CHECK_EVAL("=3-1", Value(2)); // row 65 + CHECK_EVAL("=5--2", Value(7)); // row 67 + CHECK_EVAL("=3*4", Value(12)); // row 68 + CHECK_EVAL("=2+3*4", Value(14)); // row 70 + CHECK_EVAL("=6/3", Value(2)); // row 71 + CHECK_EVAL("=5/2", Value(2.5)); // row 72 + CHECK_EVAL("=ISERROR(1/0)", Value(true)); // row 73 + CHECK_EVAL("=2^3", Value(8)); // row 74 + CHECK_EVAL("=9^0.5", Value(3)); // row 75 + CHECK_EVAL("=(-5)^3", Value(-125)); // row 76 + CHECK_EVAL("=4^-1", Value(0.25)); // row 77 + CHECK_EVAL("=5^0", Value(1)); // row 78 + CHECK_EVAL("=0^5", Value(0)); // row 79 + CHECK_EVAL("=2+3*4^2", Value(50)); // row 80 + CHECK_EVAL("=-2^2", Value(4)); // row 81 + CHECK_EVAL("=1=1", Value(true)); // row 82 + CHECK_EVAL("=1=0", Value(false)); // row 84 + CHECK_EVAL("=3=3.0001", Value(false)); // row 85 +// Not passed for line 86. + CHECK_EVAL("=\"Hi\"=\"Bye\"", Value(false)); // row 87 + CHECK_EVAL("=FALSE()=FALSE()", Value(true)); // row 88 + CHECK_EVAL("=TRUE()=FALSE()", Value(false)); // row 89 + CHECK_EVAL("=\"5\"=5", Value(false)); // row 90 + CHECK_EVAL("=TRUE()=1", Value(false)); // row 91 +// Not passed for line 92. +// Not passed for line 93. + CHECK_EVAL("=1<>1", Value(false)); // row 94 + CHECK_EVAL("=1<>2", Value(true)); // row 95 + CHECK_EVAL("=1<>\"1\"", Value(true)); // row 96 +// Not passed for line 97. + CHECK_EVAL("=5<6", Value(true)); // row 98 + CHECK_EVAL("=5<=6", Value(true)); // row 99 + CHECK_EVAL("=5>6", Value(false)); // row 100 + CHECK_EVAL("=5>=6", Value(false)); // row 101 + CHECK_EVAL("=\"A\"<\"B\"", Value(true)); // row 102 +// Not passed for line 103. + CHECK_EVAL("=\"AA\">\"A\"", Value(true)); // row 104 + CHECK_EVAL("=\"Hi \" & \"there\"", Value("Hi there")); // row 107 + CHECK_EVAL("=\"H\" & \"\"", Value("H")); // row 108 +// Not passed for line 109. + CHECK_EVAL("=50%", Value(0.5)); // row 111 + CHECK_EVAL("=20+50%", Value(20.5)); // row 112 + CHECK_EVAL("=+5", Value(5)); // row 113 + CHECK_EVAL("=+\"Hello\"", Value("Hello")); // row 114 + CHECK_EVAL("=-\"7\"", Value(-7)); // row 116 +/* + These are currently disabled, due to being locale specific. + CHECK_EVAL("=DATE(2005;1;3)=DATEVALUE(\"2005-01-03\")", Value(true)); // row 118 + CHECK_EVAL("=DATE(2017.5; 1; 2)=DATEVALUE(\"2017-01-02\")", Value(true)); // row 119 + CHECK_EVAL("=DATE(2006; 2.5; 3)=DATEVALUE(\"2006-02-03\")", Value(true)); // row 120 + CHECK_EVAL("=DATE(2006; 1; 3.5)=DATEVALUE(\"2006-01-03\")", Value(true)); // row 121 + CHECK_EVAL("=DATE(2006; 13; 3)=DATEVALUE(\"2007-01-03\")", Value(true)); // row 122 + CHECK_EVAL("=DATE(2006; 1; 32)=DATEVALUE(\"2006-02-01\")", Value(true)); // row 123 + CHECK_EVAL("=DATE(2006; 25; 34)=DATEVALUE(\"2008-02-03\")", Value(true)); // row 124 + CHECK_EVAL("=DATE(2006;-1; 1)=DATEVALUE(\"2005-11-01\")", Value(true)); // row 125 +// Not passed for line 126. +// Not passed for line 127. + CHECK_EVAL("=DATE(2004;2;29)=DATEVALUE(\"2004-02-29\")", Value(true)); // row 128 + CHECK_EVAL("=DATE(2003;2;29)=DATEVALUE(\"2003-03-01\")", Value(true)); // row 129 + CHECK_EVAL("=DATE(1904; 1; 1)=DATEVALUE(\"1904-01-01\")", Value(true)); // row 130 + CHECK_EVAL("=DATEVALUE(\"2004-12-25\")=DATE(2004;12;25)", Value(true)); // row 131 + CHECK_EVAL("=DAY(\"2006-05-21\")", Value(21)); // row 132 + CHECK_EVAL("=DAY(\"5/21/2006\")", Value(21)); // row 133 + CHECK_EVAL("=DAY(\"05-21-2006\")", Value(21)); // row 134 + CHECK_EVAL("=DAY(\"5/21/06\")", Value(21)); // row 135 + CHECK_EVAL("=DAY(\"5-21-06\")", Value(21)); // row 136 +*/ + + // functions with optional arguments + CHECK_EVAL("=ROUND(0.1)", Value(0)); + CHECK_EVAL("=ROUND(0.11;1)", Value(0.1)); + CHECK_EVAL("=ROUNDUP(0.1)", Value(1)); + CHECK_EVAL("=ROUNDUP(0.01;1)", Value(0.1)); + CHECK_EVAL("=ROUNDDOWN(0.9)", Value(0)); + CHECK_EVAL("=ROUNDDOWN(0.19;1)", Value(0.1)); +} + + + +FormulaOasisConversionTester::FormulaOasisConversionTester(): Tester() +{ +} + +QString FormulaOasisConversionTester::name() +{ + return QString("Formula (OpenDocument conversion)"); +} + +void FormulaOasisConversionTester::run() +{ + testCount = 0; + errorList.clear(); + + // cell references + CHECK_OASIS( "A1", ".A1" ); + CHECK_OASIS( "A1:A4", ".A1:.A4" ); + CHECK_OASIS( "A$1:$A4", ".A$1:.$A4" ); + CHECK_OASIS( "Sheet2!A1", "Sheet2.A1" ); + CHECK_OASIS( "'Sheet 2'!A1", "'Sheet 2'.A1" ); + CHECK_OASIS( "=A1", "=[.A1]" ); + CHECK_OASIS( "=A1:A4", "=[.A1:A4]" ); + CHECK_OASIS( "=A$1:$A4", "=[.A$1:$A4]" ); + CHECK_OASIS( "=Sheet2!A1", "=[Sheet2.A1]" ); + CHECK_OASIS( "='Sheet 2'!A1", "=['Sheet 2'.A1]" ); + + // equality + CHECK_OASIS( "=A1==A2", "=[.A1]=[.A2]" ); + + // strings + CHECK_OASIS( "=\"2,2\"+2,1+\"2,0\"", "=\"2,2\"+2.1+\"2,0\"" ); + + // decimal separator ',' + CHECK_OASIS( "=,12", "=.12" ); + CHECK_OASIS( "=12,12", "=12.12" ); + CHECK_OASIS( "=368*7*(0,1738+0,1784)*(0,1738+0,1784)", "=368*7*(0.1738+0.1784)*(0.1738+0.1784)" ); + + // function names + CHECK_OASIS( "=sum(A1;A2;A3;A4;A5)", "=sum([.A1];[.A2];[.A3];[.A4];[.A5])" ); +} + +void FormulaOasisConversionTester::checkOasis( const char *file, int line, const char* /*msg*/, + const QString& localeFormula, const QString& oasisFormula ) +{ + testCount++; + + KLocale locale("en_US"); + locale.setDecimalSymbol(","); + + // KSpread -> OpenDocument + QString formula = localeFormula; +#if 0 + Oasis::encodeFormula( formula, &locale ); + + if( formula != oasisFormula ) + { + QString message = "[Locale->Oasis] "; + message.append( "\"" + localeFormula + "\"" ); + message.append( " Result: ").append( formula ); + message.append( " Expected: ").append( oasisFormula ); + fail( file, line, message ); + } + + testCount++; +#endif + + // OpenDocument -> KSpread + formula = Oasis::decodeFormula( oasisFormula, &locale ); + + if( formula != localeFormula ) + { + QString message = "[Oasis->Locale] "; + message.append( "\"" + oasisFormula + "\"" ); + message.append( " Result: ").append( formula ); + message.append( " Expected: ").append( localeFormula ); + fail( file, line, message ); + } +} diff --git a/kspread/tests/formula_tester.h b/kspread/tests/formula_tester.h new file mode 100644 index 00000000..5efc89ff --- /dev/null +++ b/kspread/tests/formula_tester.h @@ -0,0 +1,64 @@ +/* This file is part of the KDE project + Copyright 2004 Ariya Hidayat <ariya@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KSPREAD_FORMULA_TESTER +#define KSPREAD_FORMULA_TESTER + +#include <qstring.h> +#include <kspread_value.h> + +#include "tester.h" + + +namespace KSpread +{ + +class FormulaParserTester: public Tester +{ +public: + FormulaParserTester(); + virtual QString name(); + virtual void run(); +private: + void checkParse( const char *file, int line, const char* msg, const QString&, const QString& ); +}; + +class FormulaEvalTester: public Tester +{ +public: + FormulaEvalTester(); + virtual QString name(); + virtual void run(); +private: + void checkEval( const char *file, int line, const char* msg, const QString&, const Value& ); +}; + +class FormulaOasisConversionTester : public Tester +{ +public: + FormulaOasisConversionTester(); + virtual QString name(); + virtual void run(); +private: + void checkOasis( const char *file, int line, const char* msg, const QString&, const QString& ); +}; + +} // namespace KSpread + +#endif // KSPREAD_FORMULA_TESTER diff --git a/kspread/tests/generate-openformula-tests b/kspread/tests/generate-openformula-tests new file mode 100755 index 00000000..f39ce2aa --- /dev/null +++ b/kspread/tests/generate-openformula-tests @@ -0,0 +1,44 @@ +#!/bin/bash +# +# IMPORTANT: this is like the slowest thing ever. But it works. Usually. +# Requires an open instance of KSpread, with openformula-test.ods loaded. +# + +KSPREAD=`dcopfind 'kspread*'` +KSPREAD=`echo $KSPREAD | sed s/DCOPRef\(// | sed s/,\)//` +for i in `seq 51 514` + do + CELLREF=`dcop $KSPREAD Document/Map/List1 cell 7 $i` + PASS=`dcop $CELLREF value` + if test $PASS = 1.000000 + then + CELLREF=`dcop $KSPREAD Document/Map/List1 cell 2 $i` + formula=`dcop $CELLREF text` + formula=`echo $formula | sed s/\"/\\\\\\\\\"/g` + +#nothing if formula contains a cellref ... + if test -z "`echo $formula | grep B[1-9]`" + then + CELLREF=`dcop $KSPREAD Document/Map/List1 cell 3 $i` + result=`dcop $CELLREF text` +#test whether we have a number + if test $result = True + then + VAL="true" + else if test $result = False + then + VAL="false" + else if test -z `echo $result | grep -P "^\d+([\.,]\d+)?$"` + then + VAL="\"${result}\"" + else + result=`echo $result | sed s/,/./` + VAL=$result + fi;fi;fi + echo "CHECK_EVAL(\"=${formula}\", Value($VAL)); // row $i" + fi + else echo // Not passed for line $i. + fi +done + + diff --git a/kspread/tests/inspector.cc b/kspread/tests/inspector.cc new file mode 100644 index 00000000..8996d285 --- /dev/null +++ b/kspread/tests/inspector.cc @@ -0,0 +1,261 @@ +/* This file is part of the KDE project + Copyright 2005 Ariya Hidayat <ariya@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "inspector.h" + +#include <qlayout.h> +#include <qlistview.h> +#include <qtextstream.h> + +#include <kdialogbase.h> + +#include "kspread_cell.h" +#include "kspread_style.h" +#include "kspread_sheet.h" +#include "dependencies.h" + +namespace KSpread +{ + +class Inspector::Private +{ +public: + Cell* cell; + Format* format; + Sheet* sheet; + + QListView *cellView; + QListView *formatView; + QListView *sheetView; + QListView *styleView; + QListView* depView; + + void handleCell(); + void handleFormat(); + void handleSheet(); + void handleStyle(); + void handleDep(); +}; + +} + +using namespace KSpread; + +static QString boolAsString( bool b ) +{ + if( b ) return QString( "True" ); + else return QString( "False" ); +} + +static QString longAsHexstring( long l ) +{ + return QString("%1").arg(l, 8, 16); +} + +static QString dirAsString( Sheet::LayoutDirection dir ) +{ + QString str; + switch( dir ) + { + case Sheet::LeftToRight: str = "Left to Right"; break; + case Sheet::RightToLeft: str = "Right to Left"; break; + default: str = "Unknown"; break; + } + return str; +} + +void Inspector::Private::handleCell() +{ + QString str; + + cellView->clear(); + + new QListViewItem( cellView, "Column", QString::number( cell->column() ) ); + new QListViewItem( cellView, "Row", QString::number( cell->row() ) ); + new QListViewItem( cellView, "Name", cell->name() ); + new QListViewItem( cellView, "Full Name", cell->fullName() ); + + new QListViewItem( cellView, "Default", boolAsString( cell->isDefault() ) ); + new QListViewItem( cellView, "Empty", boolAsString( cell->isEmpty() ) ); + new QListViewItem( cellView, "Formula", boolAsString( cell->isFormula() ) ); + new QListViewItem( cellView, "Format Properties", longAsHexstring( static_cast<long>( cell->format()->propertiesMask() ) ) ); + new QListViewItem( cellView, "Style Properties", longAsHexstring( static_cast<long>( cell->format()->style()->features() ) ) ); + new QListViewItem( cellView, "Text", cell->text() ); + new QListViewItem( cellView, "Text (Displayed)", + cell->strOutText().replace( QChar('\n'), "\\n" ) ); + + QTextStream ts( &str, IO_WriteOnly ); + ts << cell->value(); + new QListViewItem( cellView, "Value", str ); + + new QListViewItem( cellView, "Link", cell->link() ); + + new QListViewItem( cellView, "Width", QString::number( cell->dblWidth() ) ); + new QListViewItem( cellView, "Height", QString::number( cell->dblHeight() ) ); +} + +void Inspector::Private::handleFormat() +{ + formatView->clear(); + int col = cell->column(); + int row = cell->row(); + + new QListViewItem( formatView, "Angle", QString::number( format->getAngle(col, row) ) ); + new QListViewItem( formatView, "Multirow", boolAsString( format->multiRow(col, row) ) ); + new QListViewItem( formatView, "Protected", format->hasProperty( Format::PVerticalText ) + ? "Not specified" : boolAsString( format->isProtected(col, row) ) ); + new QListViewItem( formatView, "Vertical Text", boolAsString( format->verticalText(col, row ) ) ); + + Format::Currency currrency; + bool valid = format->currencyInfo(currrency); + new QListViewItem( formatView, "Currency symbol", valid ? currrency.symbol : "Invalid" ); + bool ok = false; + QString currencyType; + if (valid) + currencyType = Currency::getChooseString(currrency.type, ok); + new QListViewItem( formatView, "Currency type", valid && ok ? currencyType : "Invalid" ); + + QListViewItem* flags = new QListViewItem( formatView, "Flags" ); + new QListViewItem( flags, "Border (left)", + boolAsString( format->hasProperty(Format::PLeftBorder, true) ) ); + new QListViewItem( flags, "Border (right)", + boolAsString( format->hasProperty(Format::PRightBorder, true) ) ); + new QListViewItem( flags, "Border (top)", + boolAsString( format->hasProperty(Format::PTopBorder, true) ) ); + new QListViewItem( flags, "Border (bottom)", + boolAsString( format->hasProperty(Format::PBottomBorder, true) ) ); + + new QListViewItem( formatView, "Border pen width (bottom)", + QString::number( format->bottomBorderPen(col,row).width() ) ); +} + +void Inspector::Private::handleStyle() // direct style access +{ + styleView->clear(); + const Style* style = cell->format()->style(); + + QListViewItem* flags = new QListViewItem( styleView, "Flags" ); + new QListViewItem( flags, "Border (left)", + boolAsString( style->hasFeature(Style::SLeftBorder, true) ) ); + new QListViewItem( flags, "Border (right)", + boolAsString( style->hasFeature(Style::SRightBorder, true) ) ); + new QListViewItem( flags, "Border (top)", + boolAsString( style->hasFeature(Style::STopBorder, true) ) ); + new QListViewItem( flags, "Border (bottom)", + boolAsString( style->hasFeature(Style::SBottomBorder, true) ) ); + + new QListViewItem( styleView, "Border pen width (bottom)", + QString::number( style->bottomBorderPen().width() ) ); +} + +void Inspector::Private::handleSheet() +{ + sheetView->clear(); + + new QListViewItem( sheetView, "Name", sheet->sheetName() ) ; + new QListViewItem( sheetView, "Layout Direction", dirAsString( sheet->layoutDirection() ) ); +} + +void Inspector::Private::handleDep() +{ + Point cellPoint; + cellPoint.setSheet(sheet); + cellPoint.setRow( cell->row() ); + cellPoint.setColumn( cell->column() ); + + DependencyManager* manager = sheet->dependencies(); + QValueList<Point> deps = manager->getDependants( cellPoint ); + + depView->clear(); + for( unsigned i = 0; i < deps.count(); i++ ) + { + QString k1, k2; + + Point point = deps[i]; + int row = point.row(); + int column = point.column(); + k1 = Cell::fullName( point.sheet(), column, row ); + + new QListViewItem( depView, k1, k2 ); + } + +} + +Inspector::Inspector( Cell* cell ): + KDialogBase( KDialogBase::Tabbed, "Inspector", KDialogBase::Close, + KDialogBase::Close ) +{ + d = new Private; + + d->cell = cell; + d->format = cell->format(); + d->sheet = cell->sheet(); + + QFrame* cellPage = addPage( QString("Cell") ); + QVBoxLayout* cellLayout = new QVBoxLayout( cellPage, 0 ); + d->cellView = new QListView( cellPage ); + cellLayout->addWidget( d->cellView ); + d->cellView->addColumn( "Key", 150 ); + d->cellView->addColumn( "Value" ); + + QFrame* formatPage = addPage( QString("Format") ); + QVBoxLayout* formatLayout = new QVBoxLayout( formatPage, 0 ); + d->formatView = new QListView( formatPage ); + formatLayout->addWidget( d->formatView ); + d->formatView->addColumn( "Key", 150 ); + d->formatView->addColumn( "Value" ); + + QFrame* stylePage = addPage( QString("Style") ); + QVBoxLayout* styleLayout = new QVBoxLayout( stylePage, 0 ); + d->styleView = new QListView( stylePage ); + styleLayout->addWidget( d->styleView ); + d->styleView->addColumn( "Key", 150 ); + d->styleView->addColumn( "Value" ); + + QFrame* sheetPage = addPage( QString("Sheet") ); + QVBoxLayout* sheetLayout = new QVBoxLayout( sheetPage, 0 ); + d->sheetView = new QListView( sheetPage ); + sheetLayout->addWidget( d->sheetView ); + d->sheetView->addColumn( "Key", 150 ); + d->sheetView->addColumn( "Value" ); + + QFrame* depPage = addPage( QString("Dependencies") ); + QVBoxLayout* depLayout = new QVBoxLayout( depPage, 0 ); + d->depView = new QListView( depPage ); + depLayout->addWidget( d->depView ); + d->depView->addColumn( "Cell", 150 ); + d->depView->addColumn( "Content" ); + + d->handleCell(); + d->handleFormat(); + d->handleSheet(); + d->handleStyle(); + d->handleDep(); + + resize( 350, 400 ); +} + +Inspector::~Inspector() +{ + delete d; +} + +#include "inspector.moc" + + diff --git a/kspread/tests/inspector.h b/kspread/tests/inspector.h new file mode 100644 index 00000000..dd5d663e --- /dev/null +++ b/kspread/tests/inspector.h @@ -0,0 +1,46 @@ +/* This file is part of the KDE project + Copyright 2005 Ariya Hidayat <ariya@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KSPREAD_INSPECTOR +#define KSPREAD_INSPECTOR + +#include <kdialogbase.h> + +namespace KSpread +{ +class Cell; + +class Inspector : public KDialogBase +{ +Q_OBJECT + +public: + Inspector( Cell* cell ); + ~Inspector(); + +private: + class Private; + Private* d; +}; + +} + +#endif // KSPREAD_INSPECTOR + + diff --git a/kspread/tests/oasis-kspread.sh b/kspread/tests/oasis-kspread.sh new file mode 100755 index 00000000..30b00de5 --- /dev/null +++ b/kspread/tests/oasis-kspread.sh @@ -0,0 +1,87 @@ +#!/bin/bash + +# This script helps finding out problems in the OASIS loading/saving code, +# by converting .ksp -> .ods -> .ksp and comparing the initial and final .ksp files. +# We use the ksp format as a "dump" of the KSpread data, to check if everything is correct +# in memory, but the point is of course to ensure that the .ods has all the information. + +# To use this script, you need to pass the full path to an existing ksp file as argument. +# Don't use a relative path, dcopstart won't handle it +input="$1" + +# Set this to 1 in order to validate the saved oasis document using oasislint +checkoasis="1" + +appname=kspread +oldextension=ksp +oasisextension=ods +oasismimetype=application/vnd.oasis.opendocument.spreadsheet + +test -f "$input" || { echo "No such file $input"; exit 1; } + +# Load old native file +appid=`dcopstart $appname $input` +test -n "$appid" || { echo "Error starting $appname!"; exit 1; } +while `dcop $appid Document-0 isLoading` == "true"; do + sleep 1; +done + +# Save again (in case of changes in syntax etc.) +origfile=$PWD/oasisregtest-initial.$oldextension +dcop $appid Document-0 saveAs $origfile || exit 1 +test -f $origfile || exit 1 + +# Save to OASIS +tmpoasisfile=$PWD/oasisregtest.$oasisextension +dcop $appid Document-0 setOutputMimeType $oasismimetype || exit 1 +dcop $appid Document-0 saveAs $tmpoasisfile || exit 1 +test -f $tmpoasisfile || exit 1 + +dcopquit $appid + +# Load resulting OASIS file, convert to old native format +tmpnativefile=$PWD/oasisregtest-final.$oldextension +appid=`dcopstart $appname $tmpoasisfile` +while `dcop $appid Document-0 isLoading` == "true"; do + sleep 1; +done +dcop $appid Document-0 setOutputMimeType "application/x-$appname" || exit 1 +dcop $appid Document-0 saveAs $tmpnativefile || exit 1 +test -f $tmpnativefile || exit 1 + +# Unpack everything +rm -rf oasisregtest-orig +mkdir oasisregtest-orig +rm -rf oasisregtest-final +mkdir oasisregtest-final +rm -rf oasisregtest-oasis +mkdir oasisregtest-oasis +cd oasisregtest-orig || exit 1 +unzip $origfile || exit 1 +cd .. +cd oasisregtest-final || exit 1 +unzip $tmpnativefile || exit 1 +cd .. +# Validate OASIS format +cd oasisregtest-oasis || exit 1 +unzip $tmpoasisfile || exit 1 +if test "$checkoasis" = "1"; then + if type -p oasislint >/dev/null 2>&1; then + for f in content.xml styles.xml meta.xml settings.xml; do + echo "Checking $f..." ; oasislint $f + done + fi + if type -p oasislint-strict >/dev/null 2>&1; then + for f in content.xml styles.xml meta.xml settings.xml; do + echo "Checking $f strict..." && oasislint-strict $f + done + fi +fi +cd .. + +# Compare initial and final "native format" files +diff -urp oasisregtest-orig oasisregtest-final 2>&1 | tee oasisdiff | less + +echo "See oasisregtest-oasis for the OASIS xml files." +echo "For a better diffing mechanism, launch xemacs and paste into a terminal:" +echo "gnudoit '(ediff-files \"$PWD/oasisregtest-orig/maindoc.xml\" \"$PWD/oasisregtest-final/maindoc.xml\")'" diff --git a/kspread/tests/test_formula.cc b/kspread/tests/test_formula.cc new file mode 100644 index 00000000..75aff4b9 --- /dev/null +++ b/kspread/tests/test_formula.cc @@ -0,0 +1,60 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Stefan Nikolaus <stefan.nikolaus@kdemail.net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include <kapplication.h> +#include <kcmdlineargs.h> +#include <kdebug.h> + +#include "formula_tester.h" +#include "tester.h" + +using namespace KSpread; + +void run( Tester* test ) +{ + kdDebug() << test->name() << endl; + test->run(); + int failed = test->failed(); + if ( failed ) + { + kdDebug() << test->failed() << " of " << test->count() << " tests failed:" << endl; + QStringList errors = test->errors(); + QStringList::ConstIterator end = errors.end(); + for (QStringList::ConstIterator it = errors.begin(); it != end; ++it ) + { + kdDebug() << (*it) << endl; + } + } + else + { + kdDebug() << "All " << test->count() << " tests successfully passed." << endl; + } + delete test; +} + +int main( int argc, char** argv ) +{ + // Initialize command line args + KCmdLineArgs::init(argc, argv, "formulatest", "formulatest", "unit test", "0.1", true); + KApplication app(false, false); + + run( new FormulaParserTester() ); + run( new FormulaEvalTester() ); + run( new FormulaOasisConversionTester() ); +} diff --git a/kspread/tests/tester.cc b/kspread/tests/tester.cc new file mode 100644 index 00000000..91ce7fd4 --- /dev/null +++ b/kspread/tests/tester.cc @@ -0,0 +1,54 @@ +/* This file is part of the KDE project + Copyright 2004 Ariya Hidayat <ariya@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "tester.h" + +#include <qstringlist.h> +#include <qtextstream.h> + +using namespace KSpread; + +Tester::Tester() +{ + testCount = 0; +} + +unsigned Tester::count() const +{ + return testCount; +} + +QStringList Tester::errors() const +{ + return errorList; +} + +unsigned Tester::failed() const +{ + return errorList.count(); +} + +void Tester::fail( const char *file, int line, const QString& msg ) +{ + QString error; + QTextStream ts( &error, IO_WriteOnly ); + ts << file << "["<< line <<"]: " << msg; + errorList.append( error ); +} + diff --git a/kspread/tests/tester.h b/kspread/tests/tester.h new file mode 100644 index 00000000..614a93e5 --- /dev/null +++ b/kspread/tests/tester.h @@ -0,0 +1,50 @@ +/* This file is part of the KDE project + Copyright 2004 Ariya Hidayat <ariya@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KSPREAD_TESTER +#define KSPREAD_TESTER + +#include <qstring.h> +#include <qstringlist.h> + +namespace KSpread +{ + +class Tester +{ +public: + Tester(); + virtual ~Tester() {;}; + +public: + virtual QString name() = 0; + virtual void run() = 0; + unsigned count() const; + unsigned failed() const; + QStringList errors() const; + +protected: + void fail( const char *file, int line, const QString& msg ); + int testCount; + QStringList errorList; +}; + +} + +#endif // KSPREAD_TESTER diff --git a/kspread/tests/testrunner.cc b/kspread/tests/testrunner.cc new file mode 100644 index 00000000..a011e527 --- /dev/null +++ b/kspread/tests/testrunner.cc @@ -0,0 +1,132 @@ +/* This file is part of the KDE project + Copyright 2004 Ariya Hidayat <ariya@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "testrunner.h" + +#include <qapplication.h> +#include <qdict.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qtextedit.h> + +#include <kcombobox.h> +#include <kdialogbase.h> +#include <kpushbutton.h> + +#include "tester.h" +#include "value_tester.h" +#include "formula_tester.h" +//#include "stylecluster_tester.h" + +namespace KSpread +{ + +class TestRunner::Private +{ +public: + QDict<Tester> testers; + KComboBox* testType; + KPushButton* runButton; + QTextEdit* logView; +}; + +} + +using namespace KSpread; + + +TestRunner::TestRunner(): + KDialogBase( KDialogBase::Plain, "Internal Tests", KDialogBase::Close, + KDialogBase::Close ) +{ + d = new Private; + + QFrame* mainWidget = plainPage(); + QGridLayout* layout = new QGridLayout( mainWidget, 3, 4, marginHint(), spacingHint() ); + setMinimumSize( 360, 230 ); + + QLabel* typeLabel = new QLabel( "Type of Test:", mainWidget ); + layout->addWidget( typeLabel, 0, 0 ); + + d->testType = new KComboBox( mainWidget ); + layout->addWidget( d->testType, 0, 1 ); + + QSpacerItem* spacerItem = new QSpacerItem( 10, 10, QSizePolicy::Expanding, QSizePolicy::Minimum ); + layout->addItem( spacerItem, 0, 2 ); + + d->runButton = new KPushButton( "Run", mainWidget ); + layout->addWidget( d->runButton, 0, 3 ); + + d->logView = new QTextEdit( mainWidget ); + layout->addMultiCellWidget( d->logView, 2, 2, 0, 3 ); + d->logView->setTextFormat( Qt::LogText ); + + QObject::connect( d->runButton, SIGNAL( clicked() ), this, SLOT( runTest() ) ); + + // add all tests here !! + addTester( new ValueTester() ); + // addTester( new StyleClusterTester() ); + addTester( new FormulaParserTester() ); + addTester( new FormulaEvalTester() ); + addTester( new FormulaOasisConversionTester() ); +} + +TestRunner::~TestRunner() +{ + QDictIterator<Tester> it( d->testers ); + for( ; it.current(); ++it ) delete it.current(); + delete d; +} + +void TestRunner::addTester( Tester* tester ) +{ + if( !tester ) return; + d->testers.insert( tester->name(), tester ); + d->testType->insertItem( tester->name() ); +} + +void TestRunner::runTest() +{ + QString testName = d->testType->currentText(); + Tester* tester = d->testers.find( testName ); + if( tester ) + { + d->logView->clear(); + d->logView->append( QString("Test: %1").arg( testName ) ); + + QApplication::setOverrideCursor(Qt::waitCursor); + tester->run(); + QApplication::restoreOverrideCursor(); + + QStringList errorList = tester->errors(); + if( tester->failed() ) + { + d->logView->append( QString( "%1 tests, <b>%2 failed.</b>").arg( tester->count() ). + arg( tester->failed() ) ); + for( unsigned k = 0; k < errorList.count(); k++ ) + d->logView->append( errorList[k] ); + } + else + d->logView->append( QString( "%1 tests, everything is OK. ").arg( tester->count() ) ); + + d->logView->append( "Test finished." ); + } +} + +#include "testrunner.moc" diff --git a/kspread/tests/testrunner.h b/kspread/tests/testrunner.h new file mode 100644 index 00000000..0ee3a9e9 --- /dev/null +++ b/kspread/tests/testrunner.h @@ -0,0 +1,51 @@ +/* This file is part of the KDE project + Copyright 2004 Ariya Hidayat <ariya@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KSPREAD_TEST_RUNNER +#define KSPREAD_TEST_RUNNER + +#include <kdialogbase.h> + +namespace KSpread +{ + +class Tester; + +class TestRunner : public KDialogBase +{ +Q_OBJECT + +public: + TestRunner(); + ~TestRunner(); + + void addTester( Tester* tester ); + +private slots: + void runTest(); + +private: + class Private; + Private* d; +}; + +} + +#endif // KSPREAD_TEST_RUNNER + diff --git a/kspread/tests/value_tester.cc b/kspread/tests/value_tester.cc new file mode 100644 index 00000000..ce24b8ba --- /dev/null +++ b/kspread/tests/value_tester.cc @@ -0,0 +1,307 @@ +/* This file is part of the KDE project + Copyright 2004 Ariya Hidayat <ariya@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "tester.h" +#include "value_tester.h" + +#include <math.h> + +#include <kspread_value.h> + +#define CHECK(x,y) check(__FILE__,__LINE__,#x,x,y) +#define CHECK_DATE(d,x) check(__FILE__,__LINE__,d->asDate().toString().latin1(),d->asFloat(),x) + +using namespace KSpread; + +ValueTester::ValueTester(): Tester() +{ +} + +QString ValueTester::name() +{ + return QString("Value"); +} + +template<typename T> +void ValueTester::check( const char *file, int line, const char* msg, const T& result, + const T& expected ) +{ + testCount++; + if( result != expected ) + { + QString message; + QTextStream ts( &message, IO_WriteOnly ); + ts << msg; + ts << " Result:"; + ts << result; + ts << ", "; + ts << "Expected:"; + ts << expected; + fail( file, line, message ); + } +} + +void ValueTester::check( const char *file, int line, const char* msg, bool result, bool expected ) +{ + testCount++; + if( result != expected ) + { + QString message; + QTextStream ts( &message, IO_WriteOnly ); + ts << msg; + ts << " Result: "; + if( result ) ts << "True"; else ts << "False"; + ts << ", "; + ts << "Expected: "; + if( expected ) ts << "True"; else ts << "False"; + fail( file, line, message ); + } +} + +void ValueTester::run() +{ + testCount = 0; + errorList.clear(); + + Value* v1; + Value* v2; + + // empty value + v1 = new Value(); + CHECK( v1->type(), Value::Empty ); + delete v1; + + // boolean value (true) + v1 = new Value( true ); + CHECK( v1->type(), Value::Boolean ); + CHECK( v1->asBoolean(), true ); + v1->setValue( 1 ); // dummy + v1->setValue( true ); + CHECK( v1->type(), Value::Boolean ); + CHECK( v1->asBoolean(), true ); + delete v1; + + // boolean value (false) + v1 = new Value( false ); + CHECK( v1->type(), Value::Boolean ); + CHECK( v1->asBoolean(), false ); + v1->setValue( 4 ); // dummy + v1->setValue( false ); + CHECK( v1->type(), Value::Boolean ); + CHECK( v1->asBoolean(), false ); + delete v1; + + // integer value + v1 = new Value( 1977 ); + CHECK( v1->type(), Value::Integer ); + CHECK( v1->asInteger(), (long)1977 ); + v1->setValue( false ); // dummy + v1->setValue( 14 ); + CHECK( v1->type(), Value::Integer ); + CHECK( v1->isInteger(), true ); + CHECK( v1->isFloat(), false ); + CHECK( v1->isString(), false ); + CHECK( v1->isNumber(), true ); + CHECK( v1->asInteger(), (long)14 ); + delete v1; + + // floating-point value + v1 = new Value( M_PI ); + CHECK( v1->type(), Value::Float ); + CHECK( v1->asFloat(), M_PI ); + v1->setValue( false ); // dummy + v1->setValue( 14.03 ); + CHECK( v1->type(), Value::Float ); + CHECK( v1->isInteger(), false ); + CHECK( v1->isFloat(), true ); + CHECK( v1->isString(), false ); + CHECK( v1->isNumber(), true ); + CHECK( v1->asFloat(), 14.03 ); + delete v1; + + // string value + v1 = new Value( QString("Ailinon" ) ); + CHECK( v1->type(), Value::String ); + CHECK( v1->asString(), QString("Ailinon" ) ); + v1->setValue( 7 ); // dummy + v1->setValue( QString("spreadsheet" ) ); + CHECK( v1->type(), Value::String ); + CHECK( v1->isInteger(), false ); + CHECK( v1->isFloat(), false ); + CHECK( v1->isString(), true ); + CHECK( v1->isNumber(), false ); + CHECK( v1->asString(), QString("spreadsheet" ) ); + delete v1; + + // check all (valid) dates from 1900 to 2050 + // note: bail on first error immediately + QDate refDate( 1899, 12, 31 ); + v1 = new Value(); + bool date_error = 0; + for( unsigned y = 1900; !date_error && y < 2050; y++ ) + for( unsigned m = 1; !date_error && m <= 12; m++ ) + for( unsigned d = 1; !date_error && d <= 31; d++ ) + { + QDate dv1 = QDate( y, m, d ); + if( !dv1.isValid() ) continue; + double serialNo = -dv1.daysTo( refDate ) + 1.0; + v1->setValue( dv1 ); + CHECK_DATE(v1,serialNo); + date_error = v1->asFloat() != serialNo; + } + + // time value + v1 = new Value(); + v1->setValue( QTime( 0, 0, 0 ) ); + CHECK( v1->type(), Value::Integer ); + int time_error = 0; // used to save time, bail on first error + for( unsigned h = 0; !time_error && h < 24; h++ ) + for( unsigned m = 0; !time_error && m < 60; m++ ) + for( unsigned s = 0; !time_error && s < 60; s++ ) + { + QTime t1 = QTime( h, m, s ); + v1->setValue( t1 ); + QTime t2 = v1->asTime(); + if( t1.hour() != t2.hour() ) time_error++; + if( t1.minute() != t2.minute() ) time_error++; + if( t1.second() != t2.second() ) time_error++; + if( t1.msec() != t2.msec() ) time_error++; + } + CHECK( time_error, 0 ); + delete v1; + + // time value (msec) + v1 = new Value(); + v1->setValue( QTime( 0, 0, 0 ) ); + CHECK( v1->type(), Value::Integer ); + int msec_error = 0; // used to save time, bail on first error + for( unsigned ms= 0;ms < 1000;ms++ ) + { + QTime t1 = QTime( 1, 14, 2, ms ); + v1->setValue( t1 ); + QTime t2 = v1->asTime(); + if( t1.hour() != t2.hour() ) msec_error++; + if( t1.minute() != t2.minute() ) msec_error++; + if( t1.second() != t2.second() ) msec_error++; + if( t1.msec() != t2.msec() ) msec_error++; + if( msec_error ) break; + } + CHECK( msec_error, 0 ); + delete v1; + + + // TODO error values + + // TODO compare values + // TODO add, sub, mul, div values + // TODO pow + + // array + v1 = new Value( 10, 3 ); // 10 columns, 3 rows + CHECK( v1->type(), Value::Array ); + CHECK( v1->columns(), (unsigned)10 ); + CHECK( v1->rows(), (unsigned)3 ); + delete v1; + + // check empty value in array + v1 = new Value( 1, 1 ); + CHECK( v1->type(), Value::Array ); + v2 = new Value( v1->element( 0, 0 ) ); + CHECK( v2->type(), Value::Empty ); + delete v1; + + // fill simple 1x1 array + v1 = new Value( 1, 1 ); + CHECK( v1->type(), Value::Array ); + v2 = new Value( 14.3 ); + v1->setElement( 0, 0, *v2 ); + delete v2; + v2 = new Value( v1->element( 0, 0 ) ); + CHECK( v2->type(), Value::Float ); + CHECK( v2->asFloat(), 14.3 ); + delete v2; + delete v1; + + // stress test, array of 1000x1000 + v1 = new Value( 1000, 1000 ); + CHECK( v1->type(), Value::Array ); + for( unsigned c=0; c<1000; c++ ) + for( unsigned r=0; r<1000; r++ ) + { + int index = 1000*r + c; + v1->setElement( c, r, Value( index ) ); + } + int array_error = 0; + for( unsigned c=0; !array_error && c<1000; c++ ) + for( unsigned r=0; !array_error && r<1000; r++ ) + { + int index = 1000*r + c; + v2 = new Value( v1->element( c, r ) ); + if( v2->type() != Value::Integer ) array_error++; + if( v2->asInteger() != index ) array_error++; + delete v2; + } + CHECK( array_error, (int)0 ); + delete v1; + + // assignment of array value + v1 = new Value( 1, 1 ); + CHECK( v1->type(), Value::Array ); + v1->setElement( 0, 0, Value( 14.3) ); + v2 = new Value( *v1 ); // v2 is now also an array + delete v1; + v1 = new Value( v2->element( 0, 0 ) ); + CHECK( v1->type(), Value::Float ); + CHECK( v1->asFloat(), 14.3 ); + delete v1; + delete v2; + + // copy value + v1 = new Value(); + v1->setValue( 14.3 ); + v2 = new Value( *v1 ); + CHECK( v1->type(), Value::Float ); + CHECK( v2->type(), Value::Float ); + CHECK( v1->asFloat(), 14.3 ); + CHECK( v2->asFloat(), 14.3 ); + delete v1; + delete v2; + + // value assignment + v1 = new Value( 14.3 ); + v2 = new Value( true ); + v2->assign( *v1 ); + CHECK( v1->type(), Value::Float ); + CHECK( v2->type(), Value::Float ); + CHECK( v1->asFloat(), 14.3 ); + CHECK( v2->asFloat(), 14.3 ); + delete v1; + delete v2; + + // verify detachment + v1 = new Value( 14.3 ); + v2 = new Value( *v1 ); + v2->detach(); // v1 and v2 don't share data anymore + CHECK( v1->type(), Value::Float ); + CHECK( v2->type(), Value::Float ); + CHECK( v1->asFloat(), 14.3 ); + CHECK( v2->asFloat(), 14.3 ); + delete v1; + delete v2; +} diff --git a/kspread/tests/value_tester.h b/kspread/tests/value_tester.h new file mode 100644 index 00000000..bcfd75d0 --- /dev/null +++ b/kspread/tests/value_tester.h @@ -0,0 +1,44 @@ +/* This file is part of the KDE project + Copyright 2004 Ariya Hidayat <ariya@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KSPREAD_VALUE_TESTER +#define KSPREAD_VALUE_TESTER + +#include <qstring.h> + +#include "tester.h" + +namespace KSpread +{ + +class ValueTester: public KSpread::Tester +{ +public: + ValueTester(); + virtual QString name(); + virtual void run(); +private: + template<typename T> + void check( const char *file, int line, const char* msg, const T& result, const T& expected ); + void check( const char *file, int line, const char* msg, bool result, bool expected ); +}; + +} // namespace KSpread + +#endif // KSPREAD_VALUE_TESTER |