summaryrefslogtreecommitdiffstats
path: root/kspread/tests
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-20 01:29:50 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-20 01:29:50 +0000
commit8362bf63dea22bbf6736609b0f49c152f975eb63 (patch)
tree0eea3928e39e50fae91d4e68b21b1e6cbae25604 /kspread/tests
downloadkoffice-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.am33
-rw-r--r--kspread/tests/README2
-rw-r--r--kspread/tests/formula_tester.cc448
-rw-r--r--kspread/tests/formula_tester.h64
-rwxr-xr-xkspread/tests/generate-openformula-tests44
-rw-r--r--kspread/tests/inspector.cc261
-rw-r--r--kspread/tests/inspector.h46
-rwxr-xr-xkspread/tests/oasis-kspread.sh87
-rw-r--r--kspread/tests/test_formula.cc60
-rw-r--r--kspread/tests/tester.cc54
-rw-r--r--kspread/tests/tester.h50
-rw-r--r--kspread/tests/testrunner.cc132
-rw-r--r--kspread/tests/testrunner.h51
-rw-r--r--kspread/tests/value_tester.cc307
-rw-r--r--kspread/tests/value_tester.h44
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