summaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xtest-driver153
-rw-r--r--test/00-command-exit.txt9
-rw-r--r--test/01-concat-operator.txt31
-rw-r--r--test/01-exponent-operator.txt17
-rw-r--r--test/01-inline-array-div.txt35
-rw-r--r--test/01-inline-array-exp.txt35
-rw-r--r--test/01-inline-array-mul.txt35
-rw-r--r--test/01-inline-array-op-lhs.txt54
-rw-r--r--test/01-inline-array-op-rhs.txt54
-rw-r--r--test/01-inline-array-string-concat.txt19
-rw-r--r--test/01-inline-array.txt29
-rw-r--r--test/01-ranged-definitions-dups.txt37
-rw-r--r--test/01-simple-arithmetic.txt53
-rw-r--r--test/02-circular-01.txt27
-rw-r--r--test/02-circular-02.txt15
-rw-r--r--test/03-expression.txt17
-rw-r--r--test/03-leading-signs.txt22
-rw-r--r--test/04-function-abs.txt20
-rw-r--r--test/04-function-and-boolean.txt25
-rw-r--r--test/04-function-and.txt34
-rw-r--r--test/04-function-average.txt13
-rw-r--r--test/04-function-column-row.txt36
-rw-r--r--test/04-function-columns-rows.txt20
-rw-r--r--test/04-function-concatenate.txt12
-rw-r--r--test/04-function-count-formula.txt14
-rw-r--r--test/04-function-count.txt35
-rw-r--r--test/04-function-counta-edit.txt43
-rw-r--r--test/04-function-counta-static-args.txt12
-rw-r--r--test/04-function-countblank.txt38
-rw-r--r--test/04-function-exact.txt16
-rw-r--r--test/04-function-find.txt35
-rw-r--r--test/04-function-invalid-name.txt9
-rw-r--r--test/04-function-isblank.txt40
-rw-r--r--test/04-function-iserror.txt24
-rw-r--r--test/04-function-iseven.txt40
-rw-r--r--test/04-function-isformula.txt20
-rw-r--r--test/04-function-islogical.txt64
-rw-r--r--test/04-function-isna.txt20
-rw-r--r--test/04-function-isnumber.txt20
-rw-r--r--test/04-function-isref.txt18
-rw-r--r--test/04-function-istext.txt39
-rw-r--r--test/04-function-left-utf8.txt33
-rw-r--r--test/04-function-left.txt36
-rw-r--r--test/04-function-len.txt12
-rw-r--r--test/04-function-logical.txt12
-rw-r--r--test/04-function-median.txt49
-rw-r--r--test/04-function-mid-utf8.txt13
-rw-r--r--test/04-function-mid.txt28
-rw-r--r--test/04-function-mmult-inline.txt14
-rw-r--r--test/04-function-mmult.txt43
-rw-r--r--test/04-function-mode.txt26
-rw-r--r--test/04-function-n.txt26
-rw-r--r--test/04-function-nested.txt10
-rw-r--r--test/04-function-or.txt39
-rw-r--r--test/04-function-pi-int.txt11
-rw-r--r--test/04-function-replace.txt20
-rw-r--r--test/04-function-rept.txt19
-rw-r--r--test/04-function-right-utf8.txt33
-rw-r--r--test/04-function-right.txt45
-rw-r--r--test/04-function-sheet.txt31
-rw-r--r--test/04-function-sheets.txt27
-rw-r--r--test/04-function-single.txt14
-rw-r--r--test/04-function-substitute.txt34
-rw-r--r--test/04-function-t.txt74
-rw-r--r--test/04-function-textjoin.txt30
-rw-r--r--test/04-function-trim.txt21
-rw-r--r--test/04-function-true-false.txt34
-rw-r--r--test/04-function-type.txt42
-rw-r--r--test/05-range-reference.txt25
-rw-r--r--test/06-range-reference-basic-01.txt28
-rw-r--r--test/06-range-reference-basic-02.txt34
-rw-r--r--test/06-range-reference-circular-01.txt11
-rw-r--r--test/06-range-reference-unordered.txt17
-rw-r--r--test/06-range-reference-whole-column.txt17
-rw-r--r--test/06-range-reference-whole-row.txt17
-rw-r--r--test/07-fraction-numbers.txt12
-rw-r--r--test/08-boolean-cells.txt25
-rw-r--r--test/08-numeric-cells.txt17
-rw-r--r--test/09-string-cells.txt17
-rw-r--r--test/10-shared-formulas-01.txt9
-rw-r--r--test/11-reference-to-numeric-cell-01.txt23
-rw-r--r--test/12-inline-string-01.txt33
-rw-r--r--test/13-relational-operators-01.txt30
-rw-r--r--test/13-relational-operators-02.txt23
-rw-r--r--test/13-relational-operators-03.txt19
-rw-r--r--test/20-table-reference-01.txt37
-rw-r--r--test/21-named-exp-workbook-01.txt54
-rw-r--r--test/21-named-exp-worksheet-01.txt73
-rw-r--r--test/22-formulas-with-cached-results.txt15
-rw-r--r--test/22-grouped-formulas-with-cached-results.txt38
-rwxr-xr-xtest/parser-test-func.sh12
-rwxr-xr-xtest/parser-test-t0.sh9
-rwxr-xr-xtest/parser-test-t1.sh9
-rwxr-xr-xtest/parser-test-t2.sh9
-rwxr-xr-xtest/parser-test-t3.sh9
-rwxr-xr-xtest/parser-test-t4.sh9
-rwxr-xr-xtest/parser-test-t5.sh9
-rwxr-xr-xtest/parser-test-t6.sh9
-rwxr-xr-xtest/parser-test-t7.sh9
-rwxr-xr-xtest/parser-test-t8.sh9
-rwxr-xr-xtest/python/document.py242
-rwxr-xr-xtest/python/module.py40
-rw-r--r--test/thread/function-parallel.txt11
-rw-r--r--test/thread/function-wait-simple.txt8
104 files changed, 3033 insertions, 0 deletions
diff --git a/test-driver b/test-driver
new file mode 100755
index 0000000..be73b80
--- /dev/null
+++ b/test-driver
@@ -0,0 +1,153 @@
+#! /bin/sh
+# test-driver - basic testsuite driver script.
+
+scriptversion=2018-03-07.03; # UTC
+
+# Copyright (C) 2011-2021 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+# Make unconditional expansion of undefined variables an error. This
+# helps a lot in preventing typo-related bugs.
+set -u
+
+usage_error ()
+{
+ echo "$0: $*" >&2
+ print_usage >&2
+ exit 2
+}
+
+print_usage ()
+{
+ cat <<END
+Usage:
+ test-driver --test-name NAME --log-file PATH --trs-file PATH
+ [--expect-failure {yes|no}] [--color-tests {yes|no}]
+ [--enable-hard-errors {yes|no}] [--]
+ TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS]
+
+The '--test-name', '--log-file' and '--trs-file' options are mandatory.
+See the GNU Automake documentation for information.
+END
+}
+
+test_name= # Used for reporting.
+log_file= # Where to save the output of the test script.
+trs_file= # Where to save the metadata of the test run.
+expect_failure=no
+color_tests=no
+enable_hard_errors=yes
+while test $# -gt 0; do
+ case $1 in
+ --help) print_usage; exit $?;;
+ --version) echo "test-driver $scriptversion"; exit $?;;
+ --test-name) test_name=$2; shift;;
+ --log-file) log_file=$2; shift;;
+ --trs-file) trs_file=$2; shift;;
+ --color-tests) color_tests=$2; shift;;
+ --expect-failure) expect_failure=$2; shift;;
+ --enable-hard-errors) enable_hard_errors=$2; shift;;
+ --) shift; break;;
+ -*) usage_error "invalid option: '$1'";;
+ *) break;;
+ esac
+ shift
+done
+
+missing_opts=
+test x"$test_name" = x && missing_opts="$missing_opts --test-name"
+test x"$log_file" = x && missing_opts="$missing_opts --log-file"
+test x"$trs_file" = x && missing_opts="$missing_opts --trs-file"
+if test x"$missing_opts" != x; then
+ usage_error "the following mandatory options are missing:$missing_opts"
+fi
+
+if test $# -eq 0; then
+ usage_error "missing argument"
+fi
+
+if test $color_tests = yes; then
+ # Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'.
+ red='' # Red.
+ grn='' # Green.
+ lgn='' # Light green.
+ blu='' # Blue.
+ mgn='' # Magenta.
+ std='' # No color.
+else
+ red= grn= lgn= blu= mgn= std=
+fi
+
+do_exit='rm -f $log_file $trs_file; (exit $st); exit $st'
+trap "st=129; $do_exit" 1
+trap "st=130; $do_exit" 2
+trap "st=141; $do_exit" 13
+trap "st=143; $do_exit" 15
+
+# Test script is run here. We create the file first, then append to it,
+# to ameliorate tests themselves also writing to the log file. Our tests
+# don't, but others can (automake bug#35762).
+: >"$log_file"
+"$@" >>"$log_file" 2>&1
+estatus=$?
+
+if test $enable_hard_errors = no && test $estatus -eq 99; then
+ tweaked_estatus=1
+else
+ tweaked_estatus=$estatus
+fi
+
+case $tweaked_estatus:$expect_failure in
+ 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;;
+ 0:*) col=$grn res=PASS recheck=no gcopy=no;;
+ 77:*) col=$blu res=SKIP recheck=no gcopy=yes;;
+ 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;;
+ *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;;
+ *:*) col=$red res=FAIL recheck=yes gcopy=yes;;
+esac
+
+# Report the test outcome and exit status in the logs, so that one can
+# know whether the test passed or failed simply by looking at the '.log'
+# file, without the need of also peaking into the corresponding '.trs'
+# file (automake bug#11814).
+echo "$res $test_name (exit status: $estatus)" >>"$log_file"
+
+# Report outcome to console.
+echo "${col}${res}${std}: $test_name"
+
+# Register the test result, and other relevant metadata.
+echo ":test-result: $res" > $trs_file
+echo ":global-test-result: $res" >> $trs_file
+echo ":recheck: $recheck" >> $trs_file
+echo ":copy-in-global-log: $gcopy" >> $trs_file
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/test/00-command-exit.txt b/test/00-command-exit.txt
new file mode 100644
index 0000000..4b58137
--- /dev/null
+++ b/test/00-command-exit.txt
@@ -0,0 +1,9 @@
+%% Test to make sure the %exit command really exits.
+%% The 'foo' command doesn't exist, so if that line is reached the parser
+%% throws an error.
+%mode init
+A1:1
+A2:2
+%exit
+%foo
+
diff --git a/test/01-concat-operator.txt b/test/01-concat-operator.txt
new file mode 100644
index 0000000..0d58884
--- /dev/null
+++ b/test/01-concat-operator.txt
@@ -0,0 +1,31 @@
+%% Test case for concat (&) operator.
+%mode init
+A1@left
+A2@-right
+A3=A1&A2
+B1@left
+B2@&
+B3@right
+B4@&
+B5@forward
+B6=B1&B2&B3&B4&B5
+C1="("&A3&" : "&B6&")"
+C2=1&2
+%calc
+%mode result
+A3="left-right"
+B6="left&right&forward"
+C1="(left-right : left&right&forward)"
+C2="12"
+%check
+%mode edit
+A2@_or_right
+B2@_and_
+B4@_and_
+%recalc
+%mode result
+A3="left_or_right"
+B6="left_and_right_and_forward"
+C1="(left_or_right : left_and_right_and_forward)"
+%check
+%exit
diff --git a/test/01-exponent-operator.txt b/test/01-exponent-operator.txt
new file mode 100644
index 0000000..2e9de31
--- /dev/null
+++ b/test/01-exponent-operator.txt
@@ -0,0 +1,17 @@
+%% Test case for the exponent (^) operator.
+%mode init
+A4=2^3
+B1:5
+B2=A4^B1
+%calc
+%mode result
+A4=8
+B2=32768
+%check
+%mode edit
+B1:4
+%recalc
+%mode result
+B2=4096
+%check
+%exit
diff --git a/test/01-inline-array-div.txt b/test/01-inline-array-div.txt
new file mode 100644
index 0000000..08039ce
--- /dev/null
+++ b/test/01-inline-array-div.txt
@@ -0,0 +1,35 @@
+%% Test for inline array with multiplication.
+%mode init
+{A1:B2}{={30,60;90,120}/3}
+{A3:B4}{=120/{2,3;4,0}}
+{A5:B6}{=A1:B2/10}
+{A7:B8}{=240/A1:B2}
+{A9:B10}{={9,16;21,25}/{3,8;7,5}}
+{A11:D11}{={4,2,3,"C"}/{2,"A","B",2}}
+%calc
+%mode result
+A1=10
+B1=20
+A2=30
+B2=40
+A3=60
+B3=40
+A4=30
+B4=#DIV/0!
+A5=1
+B5=2
+A6=3
+B6=4
+A7=24
+B7=12
+A8=8
+B8=6
+A9=3
+B9=2
+A10=3
+B10=5
+A11=2
+B11=#VALUE!
+C11=#VALUE!
+D11=#VALUE!
+%check
diff --git a/test/01-inline-array-exp.txt b/test/01-inline-array-exp.txt
new file mode 100644
index 0000000..b6509da
--- /dev/null
+++ b/test/01-inline-array-exp.txt
@@ -0,0 +1,35 @@
+%% Test for inline array with multiplication.
+%mode init
+{A1:B2}{={1,2;3,4}^2}
+{A3:B4}{=2^{1,2;3,4}}
+{A5:B6}{=A1:B2^2}
+{A7:B8}{=2^A1:B2}
+{A9:B10}{={1,2;3,4}^{5,2;4,3}}
+{A11:D11}{={2,3,4,"C"}^{4,"A","B",2}}
+%calc
+%mode result
+A1=1
+B1=4
+A2=9
+B2=16
+A3=2
+B3=4
+A4=8
+B4=16
+A5=1
+B5=16
+A6=81
+B6=256
+A7=2
+B7=16
+A8=512
+B8=65536
+A9=1
+B9=4
+A10=81
+B10=64
+A11=16
+B11=#VALUE!
+C11=#VALUE!
+D11=#VALUE!
+%check
diff --git a/test/01-inline-array-mul.txt b/test/01-inline-array-mul.txt
new file mode 100644
index 0000000..2b7b1e2
--- /dev/null
+++ b/test/01-inline-array-mul.txt
@@ -0,0 +1,35 @@
+%% Test for inline array with multiplication.
+%mode init
+{A1:B2}{={1,2;3,4}*10}
+{A3:B4}{=10*{2,3;4,5}}
+{A5:B6}{=A1:B2*2}
+{A7:B8}{=3*A1:B2}
+{A9:B10}{={2,3;4,5}*{9,8;7,6}}
+{A11:D11}{={1,2,3,"C"}*{4,"A","B",2}}
+%calc
+%mode result
+A1=10
+B1=20
+A2=30
+B2=40
+A3=20
+B3=30
+A4=40
+B4=50
+A5=20
+B5=40
+A6=60
+B6=80
+A7=30
+B7=60
+A8=90
+B8=120
+A9=18
+B9=24
+A10=28
+B10=30
+A11=4
+B11=#VALUE!
+C11=#VALUE!
+D11=#VALUE!
+%check
diff --git a/test/01-inline-array-op-lhs.txt b/test/01-inline-array-op-lhs.txt
new file mode 100644
index 0000000..b3dfa22
--- /dev/null
+++ b/test/01-inline-array-op-lhs.txt
@@ -0,0 +1,54 @@
+%% Test for inline arrays
+%mode init
+{A1:B2}{={1,2;4,5}+2}
+{A3:B4}{={1,2;4,5}-2}
+{A5:B6}{={1,2;4,5}=2}
+{A7:B8}{={1,2;4,5}<>2}
+{A9:B10}{={1,2;4,5}<2}
+{A11:B12}{={1,2;4,5}<=2}
+{A13:B14}{={1,2;4,5}>2}
+{A15:B16}{={1,2;4,5}>=2}
+%calc
+%mode result
+%% +2
+A1=3
+B1=4
+A2=6
+B2=7
+%% -2
+A3=-1
+B3=0
+A4=2
+B4=3
+%% =2
+A5=0
+B5=1
+A6=0
+B6=0
+%% <>2
+A7=1
+B7=0
+A8=1
+B8=1
+%% <2
+A9=1
+B9=0
+A10=0
+B10=0
+%% <=2
+A11=1
+B11=1
+A12=0
+B12=0
+%% >2
+A13=0
+B13=0
+A14=1
+B14=1
+%% >=2
+A15=0
+B15=1
+A16=1
+B16=1
+%check
+
diff --git a/test/01-inline-array-op-rhs.txt b/test/01-inline-array-op-rhs.txt
new file mode 100644
index 0000000..e3590be
--- /dev/null
+++ b/test/01-inline-array-op-rhs.txt
@@ -0,0 +1,54 @@
+%% Test for inline arrays
+%mode init
+{A1:B2}{=2+{1,2;4,5}}
+{A3:B4}{=2-{1,2;4,5}}
+{A5:B6}{=2={1,2;4,5}}
+{A7:B8}{=2<>{1,2;4,5}}
+{A9:B10}{=2<{1,2;4,5}}
+{A11:B12}{=2<={1,2;4,5}}
+{A13:B14}{=2>{1,2;4,5}}
+{A15:B16}{=2>={1,2;4,5}}
+%calc
+%mode result
+%% 2+{...}
+A1=3
+B1=4
+A2=6
+B2=7
+%% 2-{...}
+A3=1
+B3=0
+A4=-2
+B4=-3
+%% 2={...}
+A5=0
+B5=1
+A6=0
+B6=0
+%% 2<>{...}
+A7=1
+B7=0
+A8=1
+B8=1
+%% 2<{...}
+A9=0
+B9=0
+A10=1
+B10=1
+%% 2<={...}
+A11=0
+B11=1
+A12=1
+B12=1
+%% 2>{...}
+A13=1
+B13=0
+A14=0
+B14=0
+%% 2>={...}
+A15=1
+B15=1
+A16=0
+B16=0
+%check
+
diff --git a/test/01-inline-array-string-concat.txt b/test/01-inline-array-string-concat.txt
new file mode 100644
index 0000000..3f5b881
--- /dev/null
+++ b/test/01-inline-array-string-concat.txt
@@ -0,0 +1,19 @@
+%% Test for inline array with multiplication.
+%mode init
+{A1:B2}{={1,2;3,4}&"A"}
+{A3:B4}{="B"&{2,3;4,"D"}}
+{A5:C5}{={"a","b","c"}&{"d","e","f"}}
+%calc
+%mode result
+A1="1A"
+B1="2A"
+A2="3A"
+B2="4A"
+A3="B2"
+B3="B3"
+A4="B4"
+B4="BD"
+A5="ad"
+B5="be"
+C5="cf"
+%check
diff --git a/test/01-inline-array.txt b/test/01-inline-array.txt
new file mode 100644
index 0000000..2da6f74
--- /dev/null
+++ b/test/01-inline-array.txt
@@ -0,0 +1,29 @@
+%% Test for inline arrays
+%mode init
+{A1:C2}{={1,2,3;4,5,6}}
+{A4:B6}{={1,2;3,4;5,6}}
+{C8:D8}{={"A","B"}}
+{B10:D11}{={-1,2,"red";-5.1,-4,"blue"}}
+%calc
+%mode result
+A1=1
+B1=2
+C1=3
+A2=4
+B2=5
+C2=6
+A4=1
+B4=2
+A5=3
+B5=4
+A6=5
+B6=6
+C8="A"
+D8="B"
+B10=-1
+C10=2
+D10="red"
+B11=-5.1
+C11=-4
+D11="blue"
+%check
diff --git a/test/01-ranged-definitions-dups.txt b/test/01-ranged-definitions-dups.txt
new file mode 100644
index 0000000..06acf37
--- /dev/null
+++ b/test/01-ranged-definitions-dups.txt
@@ -0,0 +1,37 @@
+%% Test range definitions for duplicated values.
+%mode init
+{A1:B2}=2*3
+{A3:B4}@text
+{A5:B6}:12.2
+%calc
+%mode result
+A1=6
+A2=6
+A3="text"
+A4="text"
+A5=12.2
+A6=12.2
+B1=6
+B2=6
+B3="text"
+B4="text"
+B5=12.2
+B6=12.2
+%check
+%mode edit
+{A1:B6}@overwritten
+%mode result
+A1="overwritten"
+A2="overwritten"
+A3="overwritten"
+A4="overwritten"
+A5="overwritten"
+A6="overwritten"
+B1="overwritten"
+B2="overwritten"
+B3="overwritten"
+B4="overwritten"
+B5="overwritten"
+B6="overwritten"
+%check
+%exit
diff --git a/test/01-simple-arithmetic.txt b/test/01-simple-arithmetic.txt
new file mode 100644
index 0000000..ddedb39
--- /dev/null
+++ b/test/01-simple-arithmetic.txt
@@ -0,0 +1,53 @@
+%% Test calculation involving dependency resolution.
+%mode init
+A1=1
+A2=A1+10
+A3=A2+A1*30
+A4=(10+20)*A2
+A5=A1-A2+A3*A4
+A6=A1+A3
+A7=A7
+A8=10/0
+A9=A8
+%calc
+%mode result
+A1=1
+A2=11
+A3=41
+A4=330
+A5=13520
+A6=42
+A7=#REF!
+A8=#DIV/0!
+A9=#DIV/0!
+%check
+%mode edit
+A6=A1+A2
+%recalc
+%mode result
+A1=1
+A2=11
+A3=41
+A4=330
+A5=13520
+A6=12
+A7=#REF!
+A8=#DIV/0!
+A9=#DIV/0!
+%check
+%mode edit
+A1=10
+%recalc
+%mode result
+A1=10
+A2=20
+A3=320
+A4=600
+A5=191990
+A6=30
+A7=#REF!
+A8=#DIV/0!
+A9=#DIV/0!
+%check
+%exit
+
diff --git a/test/02-circular-01.txt b/test/02-circular-01.txt
new file mode 100644
index 0000000..d6ebf6c
--- /dev/null
+++ b/test/02-circular-01.txt
@@ -0,0 +1,27 @@
+%% Test circular dependency detection.
+%mode init
+A1=20
+A2=A1+A3
+A3=A2-10
+A4=A1+35
+A5=A1*A4
+A6=A7
+A7=A8
+A8=A9
+A9=A10
+A10=A6
+%calc
+%mode result
+A1=20
+A2=#REF!
+A3=#REF!
+A4=55
+A5=1100
+A6=#REF!
+A7=#REF!
+A8=#REF!
+A9=#REF!
+A10=#REF!
+%check
+%exit
+
diff --git a/test/02-circular-02.txt b/test/02-circular-02.txt
new file mode 100644
index 0000000..a81ca42
--- /dev/null
+++ b/test/02-circular-02.txt
@@ -0,0 +1,15 @@
+%% Check for single cell circular references during edit.
+%mode init
+A1=B1
+%calc
+%mode result
+A1=0
+%check
+%mode edit
+B1=A1
+%recalc
+%mode result
+A1=#REF!
+B1=#REF!
+%check
+%exit
diff --git a/test/03-expression.txt b/test/03-expression.txt
new file mode 100644
index 0000000..4fa1581
--- /dev/null
+++ b/test/03-expression.txt
@@ -0,0 +1,17 @@
+%% Test basic expressions.
+%mode init
+A1=1
+A2=1+2-3*2
+A3=1*2+1
+A4=20*(3+4)
+A5=(20*3)+4
+%calc
+%mode result
+A1=1
+A2=-3
+A3=3
+A4=140
+A5=64
+%check
+%exit
+
diff --git a/test/03-leading-signs.txt b/test/03-leading-signs.txt
new file mode 100644
index 0000000..4c6c87b
--- /dev/null
+++ b/test/03-leading-signs.txt
@@ -0,0 +1,22 @@
+%% Test case for handling leading signs.
+%mode init
+B2=-3
+B4=-4*5
+B5=-2*-3
+B6=+4
+B7=+4+6
+A2=-B2
+A3=A2*-B7
+A4=+B5+B6
+%calc
+%mode result
+B2=-3
+B4=-20
+B5=6
+B6=4
+B7=10
+A2=3
+A3=-30
+A4=10
+%check
+
diff --git a/test/04-function-abs.txt b/test/04-function-abs.txt
new file mode 100644
index 0000000..ad60db3
--- /dev/null
+++ b/test/04-function-abs.txt
@@ -0,0 +1,20 @@
+%% Test for ABS function.
+%mode init
+A1=2
+A2=-4.2
+A3=9
+A4=-5.8
+B1=ABS(A1)
+B2=ABS(A2)
+B3=ABS(A3)
+B4=ABS(A4)
+B5=ABS(A2+A4)
+%calc
+%mode result
+B1=2
+B2=4.2
+B3=9
+B4=5.8
+B5=10
+%check
+%exit
diff --git a/test/04-function-and-boolean.txt b/test/04-function-and-boolean.txt
new file mode 100644
index 0000000..f1b3241
--- /dev/null
+++ b/test/04-function-and-boolean.txt
@@ -0,0 +1,25 @@
+%% AND function with boolean values.
+%mode init
+A1:true
+A2:true
+A3:false
+A4:true
+A5:true
+B1=AND(A1)
+B2=AND(A1:A2)
+B3=AND(A1:A3)
+B4=AND(A1:A10)
+B5=AND(A4:A10)
+B6=AND(A3,A4,A5)
+B7=AND(A1,A4)
+%calc
+%mode result
+B1=true
+B2=true
+B3=false
+B4=false
+B5=true
+B6=false
+B7=true
+%check
+%exit
diff --git a/test/04-function-and.txt b/test/04-function-and.txt
new file mode 100644
index 0000000..74300f9
--- /dev/null
+++ b/test/04-function-and.txt
@@ -0,0 +1,34 @@
+%% AND function
+%mode init
+A1:12
+A2:23
+A3:34
+A4:0
+A11=1
+A12=2
+A13=3
+A14=0
+A20@string value
+A21="string result"
+B1=AND(A1:A10)
+B2=AND(A1:A3)
+B3=AND(A11:A13)
+B4=AND(A11:A14)
+B5=AND(1,2,3)
+B6=AND(2,0,-1)
+B7=AND(2,"str",22)
+B8=AND(A1,A2,A3,A20,A21)
+B9=AND(A1,A14,A21)
+%calc
+%mode result
+B1=false
+B2=true
+B3=true
+B4=false
+B5=true
+B6=false
+B7=true
+B8=true
+B9=false
+%check
+%exit
diff --git a/test/04-function-average.txt b/test/04-function-average.txt
new file mode 100644
index 0000000..b56469d
--- /dev/null
+++ b/test/04-function-average.txt
@@ -0,0 +1,13 @@
+%% Test for AVERAGE function.
+%mode init
+A1=2
+A2=4
+A3=9
+A4=AVERAGE(A1:A3)
+A5=AVERAGE(A1,A2,A3)
+%calc
+%mode result
+A4=5
+A5=5
+%check
+%exit
diff --git a/test/04-function-column-row.txt b/test/04-function-column-row.txt
new file mode 100644
index 0000000..61dc452
--- /dev/null
+++ b/test/04-function-column-row.txt
@@ -0,0 +1,36 @@
+%mode init
+A1=COLUMN()
+B2=COLUMN()
+C3=COLUMN()
+D1=COLUMN(A1)
+D2=COLUMN(B10)
+D4=COLUMN(B2:B5)
+G3=COLUMN(C1:F3)
+%calc
+%mode result
+A1=1
+B2=2
+C3=3
+D1=1
+D2=2
+D4=2
+G3=3
+%check
+%mode edit
+A1=ROW()
+B2=ROW()
+C3=ROW()
+D1=ROW(A1)
+D2=ROW(B10)
+D4=ROW(B2:B5)
+G3=ROW(C1:F3)
+%recalc
+%mode result
+A1=1
+B2=2
+C3=3
+D1=1
+D2=10
+D4=2
+G3=1
+%check
diff --git a/test/04-function-columns-rows.txt b/test/04-function-columns-rows.txt
new file mode 100644
index 0000000..388c348
--- /dev/null
+++ b/test/04-function-columns-rows.txt
@@ -0,0 +1,20 @@
+%mode init
+A1=COLUMNS(A2:B10)
+A2=COLUMNS(C1)
+A3=COLUMNS(B2,A4:D100)
+%calc
+%mode result
+A1=2
+A2=1
+A3=5
+%check
+%mode edit
+A1=ROWS(A2:B10)
+A2=ROWS(C1)
+A3=ROWS(B2,A4:D100)
+%recalc
+%mode result
+A1=9
+A2=1
+A3=98
+%check
diff --git a/test/04-function-concatenate.txt b/test/04-function-concatenate.txt
new file mode 100644
index 0000000..2ee1271
--- /dev/null
+++ b/test/04-function-concatenate.txt
@@ -0,0 +1,12 @@
+%% Test built-in functions.
+%mode init
+A1:1
+A2@One
+A3=CONCATENATE(A1," ",A2)
+%calc
+%mode result
+A1=1
+A2="One"
+A3="1 One"
+%check
+%exit
diff --git a/test/04-function-count-formula.txt b/test/04-function-count-formula.txt
new file mode 100644
index 0000000..4d70675
--- /dev/null
+++ b/test/04-function-count-formula.txt
@@ -0,0 +1,14 @@
+%% Another COUNT function test that involves formula cells as inputs.
+%mode init
+A1="str"
+A2=1+2
+A3=A2*2
+B1=COUNT(A1,A2,A3)
+B2=COUNT(A1:A3)
+B3=COUNT(B1:B2)
+%calc
+%mode result
+B1=2
+B2=2
+B3=2
+%check
diff --git a/test/04-function-count.txt b/test/04-function-count.txt
new file mode 100644
index 0000000..0b14e3b
--- /dev/null
+++ b/test/04-function-count.txt
@@ -0,0 +1,35 @@
+%% Test for COUNT function. COUNT only counts numeric cells whereas COUNTA
+%% also counts string cells.
+%mode init
+A1:1
+A2:2
+A3:3
+B1@A
+B2@B
+B3@C
+C1:true
+C2:false
+C3:false
+D1=COUNT(A1:A3)
+D2=COUNT(A1:A4)
+D3=COUNT(A1:B3)
+D4=COUNT(A1:C3)
+D5=COUNT(A1:C1)
+D6=COUNT(A100:A120)
+D7=COUNT(A:A)
+D8=COUNT(1,2,3,4)
+D9=COUNT(1,"str",3)
+D10=COUNT()
+%calc
+%mode result
+D1=3
+D2=3
+D3=3
+D4=6
+D5=2
+D6=0
+D7=3
+D8=4
+D9=2
+D10=0
+%check
diff --git a/test/04-function-counta-edit.txt b/test/04-function-counta-edit.txt
new file mode 100644
index 0000000..f6f3fc1
--- /dev/null
+++ b/test/04-function-counta-edit.txt
@@ -0,0 +1,43 @@
+%% Test for COUNTA function.
+%mode init
+A1:1
+A2:2
+A3:3
+B1@A
+B2@B
+B3@C
+C1=COUNTA(A1:B6)
+%calc
+%mode result
+C1=6
+%check
+%mode edit
+A2@Two
+A4@Four
+%recalc
+%mode result
+C1=7
+%check
+%mode edit
+B6:10
+B7:11
+%recalc
+%mode result
+C1=8
+%check
+%mode edit
+A6=18+3
+A7=2/3
+%recalc
+%mode result
+A6=21
+C1=9
+%check
+%mode edit
+C2=COUNTA(A3:B3)
+%recalc
+%mode result
+C1=9
+C2=2
+%check
+%exit
diff --git a/test/04-function-counta-static-args.txt b/test/04-function-counta-static-args.txt
new file mode 100644
index 0000000..19d8c0c
--- /dev/null
+++ b/test/04-function-counta-static-args.txt
@@ -0,0 +1,12 @@
+%% Test for COUNTA() function with static value arguments.
+%mode init
+A1=COUNTA(1,2,3)
+A2=COUNTA(1,"str",3)
+A3=COUNTA()
+%calc
+%mode result
+A1=3
+A2=3
+A3=0
+%check
+
diff --git a/test/04-function-countblank.txt b/test/04-function-countblank.txt
new file mode 100644
index 0000000..7ce2706
--- /dev/null
+++ b/test/04-function-countblank.txt
@@ -0,0 +1,38 @@
+%% --------------------------------------------------------------------------
+%mode session
+row-limit:1000
+column-limit:100
+%% --------------------------------------------------------------------------
+%mode init
+A1:1
+A2:2
+A3:3
+D1=COUNTBLANK(A1:A3)
+D2=COUNTBLANK(A3)
+D3=COUNTBLANK(A1:A4)
+D4=COUNTBLANK(B1:B10)
+D5=COUNTBLANK(A4)
+D6=COUNTBLANK(B2)
+D7=COUNTBLANK(E:E)
+%calc
+%% --------------------------------------------------------------------------
+%mode result
+D1=0
+D2=0
+D3=1
+D4=10
+D5=1
+D6=1
+D7=1000
+%check
+%% --------------------------------------------------------------------------
+%mode edit
+E5:20
+E10:100
+%recalc
+%mode result
+D7=998
+%check
+%% --------------------------------------------------------------------------
+
+
diff --git a/test/04-function-exact.txt b/test/04-function-exact.txt
new file mode 100644
index 0000000..3b996ef
--- /dev/null
+++ b/test/04-function-exact.txt
@@ -0,0 +1,16 @@
+%mode init
+A1=EXACT("aaa", "aaa")
+A2=EXACT("AaA", "aAa")
+A3=EXACT(1234, 1234)
+A4=EXACT(1234, "1234")
+B1@salvation
+B2="salvation"
+B3=EXACT(B1, B2)
+%calc
+%mode result
+A1=true
+A2=false
+A3=true
+A4=true
+B3=true
+%check
diff --git a/test/04-function-find.txt b/test/04-function-find.txt
new file mode 100644
index 0000000..f458168
--- /dev/null
+++ b/test/04-function-find.txt
@@ -0,0 +1,35 @@
+%mode init
+A1@ABCDEFGABC
+A2=FIND("ABC",A1)
+A3=FIND("ABC",A1,2)
+A4=FIND("Z",A1)
+A5=FIND("BC",A1,100)
+A6=FIND("BC",A1, 2.9)
+A7=FIND("BC",A1, 3.9)
+B1@かささぎの渡せる橋におく霜の白きを見れば夜ぞふけにける
+B2=FIND("かささぎ",B1)
+B3=FIND("渡せる橋",B1)
+B4=FIND("ふけにける",B1)
+C1@ダミー文章。ダミー文章。
+C2=FIND("ミー",C1)
+C3=FIND("ミー",C1,2)
+C4=FIND("ミー",C1,3)
+C5=FIND("ミー",C1,8)
+C6=FIND("ミー",C1,9)
+%calc
+%mode result
+A2=1
+A3=8
+A4=#VALUE!
+A5=#VALUE!
+A6=2
+A7=9
+B2=1
+B3=6
+B4=23
+C2=2
+C3=2
+C4=8
+C5=8
+C6=#VALUE!
+%check
diff --git a/test/04-function-invalid-name.txt b/test/04-function-invalid-name.txt
new file mode 100644
index 0000000..34098d1
--- /dev/null
+++ b/test/04-function-invalid-name.txt
@@ -0,0 +1,9 @@
+%% invalid function name should produce the result of #NAME?.
+%mode init
+A1=Foo()
+%calc
+%mode result
+A1=#NAME?
+%check
+%exit
+
diff --git a/test/04-function-isblank.txt b/test/04-function-isblank.txt
new file mode 100644
index 0000000..30a320d
--- /dev/null
+++ b/test/04-function-isblank.txt
@@ -0,0 +1,40 @@
+%% Test for ISBLANK function.
+%mode init
+A1:2
+A2=A1
+A3@
+B1=ISBLANK(A1)
+B2=ISBLANK(A2)
+B3=ISBLANK(A3)
+B4=ISBLANK(A4)
+B5=ISBLANK(1)
+B6=ISBLANK("string")
+C1:2
+C2:3
+C6@str1
+C7@str2
+C10:10
+D1=ISBLANK(C1:C3)
+D2=ISBLANK(C3:C5)
+D3=ISBLANK(C3:C6)
+D4=ISBLANK(C2:C6)
+D5=ISBLANK(C7:C8)
+D6=ISBLANK(C8:C9)
+D7=ISBLANK(C11:C13)
+%calc
+%mode result
+B1=false
+B2=false
+B3=false
+B4=true
+B5=false
+B6=false
+D1=false
+D2=true
+D3=false
+D4=false
+D5=false
+D6=true
+D7=true
+%check
+%exit
diff --git a/test/04-function-iserror.txt b/test/04-function-iserror.txt
new file mode 100644
index 0000000..5346e0d
--- /dev/null
+++ b/test/04-function-iserror.txt
@@ -0,0 +1,24 @@
+%% Test case for built-in ISERROR function.
+%mode init
+A1:10
+A2=10/2
+A3=2/0
+A4=NonExistingName
+B1=ISERROR(A1)
+B2=ISERROR(A2)
+B3=ISERROR(A3)
+B4=ISERROR(A4)
+C4=ISERROR(5.2)
+C5=ISERROR("inline text")
+C6=ISERROR(NA())
+%calc
+%mode result
+B1=false
+B2=false
+B3=true
+B4=true
+C4=false
+C5=false
+C6=true
+%check
+
diff --git a/test/04-function-iseven.txt b/test/04-function-iseven.txt
new file mode 100644
index 0000000..aa224e2
--- /dev/null
+++ b/test/04-function-iseven.txt
@@ -0,0 +1,40 @@
+%% Test for ISEVEN and ISODD functions.
+%mode init
+A2=10.2
+A3=11.4
+B2=ISEVEN(2)
+B3=ISEVEN(3)
+B4=ISEVEN(1.2)
+B5=ISEVEN(4.2)
+B6=ISEVEN(4.9)
+B7=ISEVEN(-4.9)
+B8=ISEVEN(A2)
+B9=ISEVEN(A3)
+C2=ISODD(2)
+C3=ISODD(3)
+C4=ISODD(1.2)
+C5=ISODD(4.2)
+C6=ISODD(4.9)
+C7=ISODD(-4.9)
+C8=ISODD(A2)
+C9=ISODD(A3)
+%calc
+%mode result
+B2=true
+B3=false
+B4=false
+B5=true
+B6=true
+B7=true
+B8=true
+B9=false
+C2=false
+C3=true
+C4=true
+C5=false
+C6=false
+C7=false
+C8=false
+C9=true
+%check
+%exit
diff --git a/test/04-function-isformula.txt b/test/04-function-isformula.txt
new file mode 100644
index 0000000..f283b54
--- /dev/null
+++ b/test/04-function-isformula.txt
@@ -0,0 +1,20 @@
+%% Test for ISFORMULA function.
+%mode init
+A1:1
+A2@string
+A3="string"
+A4=1/2
+B1=ISFORMULA(A1)
+B2=ISFORMULA(A2)
+B3=ISFORMULA(A3)
+B4=ISFORMULA(A4)
+B5=ISFORMULA(A5)
+%calc
+%mode result
+B1=false
+B2=false
+B3=true
+B4=true
+B5=false
+%check
+%exit
diff --git a/test/04-function-islogical.txt b/test/04-function-islogical.txt
new file mode 100644
index 0000000..2673409
--- /dev/null
+++ b/test/04-function-islogical.txt
@@ -0,0 +1,64 @@
+%% All values in column A are expected to be logical.
+%% All values in column B are expected to be non-logical.
+%mode init
+A1:true
+A2:false
+A3=ISBLANK(A1)
+A4=ISERROR(A2)
+A5=ISEVEN(A1)
+A6=ISODD(A1)
+A7=ISFORMULA(A2)
+A8=ISNUMBER(A1)
+A9=ISREF(A1)
+A10=ISTEXT(A3)
+A11=ISNONTEXT(A3)
+A12=A1
+A13=A2
+%% B1 is empty
+B2:12.4
+B3@string value
+B4=1/2
+C1=ISLOGICAL(A1)
+C2=ISLOGICAL(A2)
+C3=ISLOGICAL(A3)
+C4=ISLOGICAL(A4)
+C5=ISLOGICAL(A5)
+C6=ISLOGICAL(A6)
+C7=ISLOGICAL(A7)
+C8=ISLOGICAL(A8)
+C9=ISLOGICAL(A9)
+C10=ISLOGICAL(A10)
+C11=ISLOGICAL(A11)
+C12=ISLOGICAL(A12)
+C13=ISLOGICAL(A13)
+C43=ISLOGICAL(1)
+C44=ISLOGICAL("string value")
+C45=ISLOGICAL(A100)
+C46=ISLOGICAL(C1)
+D1=ISLOGICAL(B1)
+D2=ISLOGICAL(B2)
+D3=ISLOGICAL(B3)
+D4=ISLOGICAL(B4)
+%calc
+%mode result
+C1=true
+C2=true
+C3=true
+C4=true
+C5=true
+C6=true
+C7=true
+C8=true
+C9=true
+C10=true
+C11=true
+C12=true
+C13=true
+C43=false
+C44=false
+C45=false
+C46=true
+D1=false
+D2=false
+D3=false
+%check
diff --git a/test/04-function-isna.txt b/test/04-function-isna.txt
new file mode 100644
index 0000000..148c649
--- /dev/null
+++ b/test/04-function-isna.txt
@@ -0,0 +1,20 @@
+%mode init
+B1=NA()
+B2=B1
+B3=ISNA(B1)
+B4=ISNA(B2)
+B5=ISNA(B3)
+B6=ISNA(1)
+B7=ISNA("str")
+B8=ISNA(NA())
+%calc
+%mode result
+B1=#N/A
+B2=#N/A
+B3=true
+B4=true
+B5=false
+B6=false
+B7=false
+B8=true
+%check
diff --git a/test/04-function-isnumber.txt b/test/04-function-isnumber.txt
new file mode 100644
index 0000000..e1a7e19
--- /dev/null
+++ b/test/04-function-isnumber.txt
@@ -0,0 +1,20 @@
+%% Test for ISNUMBER function.
+%mode init
+A1:345
+A2=2
+A3=CONCATENATE("string", " ", "formula")
+A4@string cell
+B1=ISNUMBER(A1)
+B2=ISNUMBER(A2)
+B3=ISNUMBER(A3)
+B4=ISNUMBER(A4)
+B5=ISNUMBER(A5)
+%calc
+%mode result
+B1=true
+B2=true
+B3=false
+B4=false
+B5=false
+%check
+%exit
diff --git a/test/04-function-isref.txt b/test/04-function-isref.txt
new file mode 100644
index 0000000..08e3c20
--- /dev/null
+++ b/test/04-function-isref.txt
@@ -0,0 +1,18 @@
+%% Test for ISREF function.
+%mode init
+C5=ISREF(A2)
+C6=ISREF(A1:A10)
+C7=ISREF("inline text")
+C8=ISREF(42)
+C9=ISREF(CONCATENATE("this ", "is ", "text"))
+C10=ISREF(A1 + 10)
+%calc
+%mode result
+C5=true
+C6=true
+C7=false
+C8=false
+C9=false
+C10=false
+%check
+%exit
diff --git a/test/04-function-istext.txt b/test/04-function-istext.txt
new file mode 100644
index 0000000..59286b0
--- /dev/null
+++ b/test/04-function-istext.txt
@@ -0,0 +1,39 @@
+%% Test for ISTEXT and ISNONTEXT functions.
+%% ISNONTEXT is true where ISTEXT is false, and vice versa.
+%mode init
+A1:1
+A2=1
+A3="text"
+A4@text
+B1=ISTEXT(A1)
+B2=ISTEXT(A2)
+B3=ISTEXT(A3)
+B4=ISTEXT(A4)
+B5=ISTEXT(A5)
+B6=ISTEXT(1)
+B7=ISTEXT("inline text")
+C1=ISNONTEXT(A1)
+C2=ISNONTEXT(A2)
+C3=ISNONTEXT(A3)
+C4=ISNONTEXT(A4)
+C5=ISNONTEXT(A5)
+C6=ISNONTEXT(1)
+C7=ISNONTEXT("inline text")
+%calc
+%mode result
+B1=false
+B2=false
+B3=true
+B4=true
+B5=false
+B6=false
+B7=true
+C1=true
+C2=true
+C3=false
+C4=false
+C5=true
+C6=true
+C7=false
+%check
+%exit
diff --git a/test/04-function-left-utf8.txt b/test/04-function-left-utf8.txt
new file mode 100644
index 0000000..5c69d20
--- /dev/null
+++ b/test/04-function-left-utf8.txt
@@ -0,0 +1,33 @@
+%mode init
+A1@今日は姉の誕生日です。
+B1=LEFT(A1,0)
+B2=LEFT(A1,1)
+B3=LEFT(A1,2)
+B4=LEFT(A1,3)
+B5=LEFT(A1,4)
+B6=LEFT(A1,5)
+B7=LEFT(A1,6)
+B8=LEFT(A1,7)
+B9=LEFT(A1,8)
+B10=LEFT(A1,9)
+B11=LEFT(A1,10)
+B12=LEFT(A1,11)
+B13=LEFT(A1,12)
+B14=LEFT(A1,-1)
+%calc
+%mode result
+B1=""
+B2="今"
+B3="今日"
+B4="今日は"
+B5="今日は姉"
+B6="今日は姉の"
+B7="今日は姉の誕"
+B8="今日は姉の誕生"
+B9="今日は姉の誕生日"
+B10="今日は姉の誕生日で"
+B11="今日は姉の誕生日です"
+B12="今日は姉の誕生日です。"
+B13="今日は姉の誕生日です。"
+B14=#VALUE!
+%check
diff --git a/test/04-function-left.txt b/test/04-function-left.txt
new file mode 100644
index 0000000..9bdcd5d
--- /dev/null
+++ b/test/04-function-left.txt
@@ -0,0 +1,36 @@
+%% Test built-in function LEFT.
+%mode init
+A1@ABCDE
+A2:12345
+B1=LEFT(A1)
+B2=LEFT(A1,2)
+B3=LEFT(A1,3)
+B4=LEFT(A1,4)
+B5=LEFT(A1,5)
+B6=LEFT(A1,6)
+B7=LEFT(B6,3)
+C1=LEFT(A2)
+C2=LEFT(A2,2)
+C3=LEFT(A2,3)
+C4=LEFT(A2,4)
+C5=LEFT(A2,5)
+C6=LEFT(A2,6)
+C7=LEFT(C6,2)
+%calc
+%mode result
+B1="A"
+B2="AB"
+B3="ABC"
+B4="ABCD"
+B5="ABCDE"
+B6="ABCDE"
+B7="ABC"
+C1="1"
+C2="12"
+C3="123"
+C4="1234"
+C5="12345"
+C6="12345"
+C7="12"
+%check
+%exit
diff --git a/test/04-function-len.txt b/test/04-function-len.txt
new file mode 100644
index 0000000..c43726f
--- /dev/null
+++ b/test/04-function-len.txt
@@ -0,0 +1,12 @@
+%mode init
+A1@富士の高嶺に
+A2="雪はふりつつ"
+B1=LEN(A1)
+B2=LEN(A2)
+B3=LEN("ふりさけ見れば春日なる")
+%calc
+%mode result
+B1=6
+B2=6
+B3=11
+%check
diff --git a/test/04-function-logical.txt b/test/04-function-logical.txt
new file mode 100644
index 0000000..86df420
--- /dev/null
+++ b/test/04-function-logical.txt
@@ -0,0 +1,12 @@
+%% Test case for built-in logical functions.
+%mode init
+A1:2
+A2:3
+A3=if(A1=A2,"equal","not equal")
+A4=if(A1<>A2,"not equal","equal")
+%calc
+%mode result
+A3="not equal"
+A4="not equal"
+%check
+%exit
diff --git a/test/04-function-median.txt b/test/04-function-median.txt
new file mode 100644
index 0000000..c840053
--- /dev/null
+++ b/test/04-function-median.txt
@@ -0,0 +1,49 @@
+%mode init
+A1:5
+A2:2
+A3:3
+A4=8
+A5:1
+A6=6
+A7:7
+A8=4
+A9:9
+A10:10
+A12=MEDIAN(A$1:A1)
+A13=MEDIAN(A$1:A2)
+A14=MEDIAN(A$1:A3)
+A15=MEDIAN(A$1:A4)
+A16=MEDIAN(A$1:A5)
+A17=MEDIAN(A$1:A6)
+A18=MEDIAN(A$1:A7)
+A19=MEDIAN(A$1:A8)
+A20=MEDIAN(A$1:A9)
+A21=MEDIAN(A$1:A10)
+A22=MEDIAN(1,9,2,3,5)
+A23=MEDIAN(4,2,1,3)
+A24=MEDIAN(A1,A2,A3,A4,A5,A6,A7,A8,A9,A10)
+A25=MEDIAN(42)
+B1=TRUE()
+B2=FALSE()
+B3=TRUE()
+B5=MEDIAN(B1:B3)
+B6=MEDIAN(B1:B2)
+%calc
+%mode result
+A12=5
+A13=3.5
+A14=3
+A15=4
+A16=3
+A17=4
+A18=5
+A19=4.5
+A20=5
+A21=5.5
+A22=3
+A23=2.5
+A24=5.5
+A25=42
+B5=1
+B6=0.5
+%check
diff --git a/test/04-function-mid-utf8.txt b/test/04-function-mid-utf8.txt
new file mode 100644
index 0000000..9208a68
--- /dev/null
+++ b/test/04-function-mid-utf8.txt
@@ -0,0 +1,13 @@
+%mode init
+A1@秋の田のかりほの庵の苫をあらみわが衣手は露にぬれつつ
+B1=MID(A1,1,4)
+B2=MID(A1,16,5)
+B3=MID(A1,21,6)
+B4=MID(A1,23,10)
+%calc
+%mode result
+B1="秋の田の"
+B2="わが衣手は"
+B3="露にぬれつつ"
+B4="ぬれつつ"
+%check
diff --git a/test/04-function-mid.txt b/test/04-function-mid.txt
new file mode 100644
index 0000000..9385eb6
--- /dev/null
+++ b/test/04-function-mid.txt
@@ -0,0 +1,28 @@
+%mode init
+A1@ABCDE
+A2:12345
+B1=MID(A1,1,5)
+B2=MID(A1,1,6)
+B3=MID(A1,2,3)
+B4=MID(A1,3,3)
+B5=MID(A1,3,0)
+B6=MID(A1,0,1)
+B7=MID(A1,10,2)
+C1=MID(A2,1,5)
+C2=MID(A2,1,6)
+C3=MID(A2,2,3)
+C4=MID(A2,5,10)
+%calc
+%mode result
+B1="ABCDE"
+B2="ABCDE"
+B3="BCD"
+B4="CDE"
+B5=""
+B6=#VALUE!
+B7=""
+C1="12345"
+C2="12345"
+C3="234"
+C4="5"
+%check
diff --git a/test/04-function-mmult-inline.txt b/test/04-function-mmult-inline.txt
new file mode 100644
index 0000000..62b59ca
--- /dev/null
+++ b/test/04-function-mmult-inline.txt
@@ -0,0 +1,14 @@
+%mode init
+{A1:C3}{=mmult({1;2;3},{4,5,6})}
+%calc
+%mode result
+A1=4
+B1=5
+C1=6
+A2=8
+B2=10
+C2=12
+A3=12
+B3=15
+C3=18
+%check
diff --git a/test/04-function-mmult.txt b/test/04-function-mmult.txt
new file mode 100644
index 0000000..84eda1c
--- /dev/null
+++ b/test/04-function-mmult.txt
@@ -0,0 +1,43 @@
+%% Test built-in MMULT function which performs a matrix multiplication whose
+%% result is a matrix spanning over a range of cells.
+%mode init
+A1:1
+A2:2
+A3:3
+C1:4
+D1:5
+E1:6
+{C5:E7}{=MMULT(A1:A3,C1:E1)}
+E11=E7*10
+%calc
+%print dependency
+%mode result
+C5=4
+D5=5
+E5=6
+C6=8
+D6=10
+E6=12
+C7=12
+D7=15
+E7=18
+E11=180
+%check
+%mode edit
+A1:2
+A2:4
+A3:6
+%recalc
+%mode result
+C5=8
+D5=10
+E5=12
+C6=16
+D6=20
+E6=24
+C7=24
+D7=30
+E7=36
+E11=360
+%check
+%exit
diff --git a/test/04-function-mode.txt b/test/04-function-mode.txt
new file mode 100644
index 0000000..18ef512
--- /dev/null
+++ b/test/04-function-mode.txt
@@ -0,0 +1,26 @@
+%mode init
+A1=MODE(5,1,2,2,3,4,5,5,3,3)
+A2=MODE(1,1)
+A3=MODE(1)
+A4=MODE(1,3,2)
+A5=MODE("A","B")
+C1:4
+C2:9
+C3=4
+C4:9
+C5:2
+C6:0
+C7=10
+C8:9
+C9@string
+C10:9
+C12=MODE(C1:C4,C5:C10)
+%calc
+%mode result
+A1=3
+A2=1
+A3=#N/A
+A4=#N/A
+A5=#N/A
+C12=9
+%check
diff --git a/test/04-function-n.txt b/test/04-function-n.txt
new file mode 100644
index 0000000..28585b6
--- /dev/null
+++ b/test/04-function-n.txt
@@ -0,0 +1,26 @@
+%mode init
+A1=N(12)
+A2=N("str")
+A3=N(TRUE())
+A4=N(FALSE())
+B1:12
+B2:true
+B3:false
+B4@string value
+C1=N(B1)
+C2=N(B2)
+C3=N(B3)
+C4=N(B4)
+C5=N(B5)
+%calc
+%mode result
+A1=12
+A2=0
+A3=1
+A4=0
+C1=12
+C2=1
+C3=0
+C4=0
+C5=0
+%check
diff --git a/test/04-function-nested.txt b/test/04-function-nested.txt
new file mode 100644
index 0000000..f12ab6d
--- /dev/null
+++ b/test/04-function-nested.txt
@@ -0,0 +1,10 @@
+%% Test case for nested functions.
+%mode init
+A5=MIN(MIN(2, 4), MAX(1,8))
+A6=CONCATENATE("min: ", MIN(A5, 4), "; max: ", MAX(1, 8))
+%calc
+%mode result
+A5=2
+A6="min: 2; max: 8"
+%check
+%exit
diff --git a/test/04-function-or.txt b/test/04-function-or.txt
new file mode 100644
index 0000000..3276661
--- /dev/null
+++ b/test/04-function-or.txt
@@ -0,0 +1,39 @@
+%% Test case for OR function.
+%mode init
+A1:1
+A2:0
+A4:0
+A5=1
+A6=0
+A7:true
+A8:false
+A9@string value is ignored
+B1=OR(A1,A2)
+B2=OR(A1:A2)
+B3=OR(A2,A4)
+B4=OR(A2:A4)
+B5=OR(A2:A4,A1)
+B6=OR(A5,A6)
+B7=OR(A5:A6,A2)
+B8=OR(B1:B7)
+B9=OR(A7,A8)
+B10=OR(A8:A10)
+B11=OR(A9,A1)
+B12=OR(A9,A2)
+B13=OR(A1:A10)
+%calc
+%mode result
+B1=true
+B2=true
+B3=false
+B4=false
+B5=true
+B6=true
+B7=true
+B8=true
+B9=true
+B10=false
+B11=true
+B12=false
+B13=true
+%check
diff --git a/test/04-function-pi-int.txt b/test/04-function-pi-int.txt
new file mode 100644
index 0000000..2d59ba3
--- /dev/null
+++ b/test/04-function-pi-int.txt
@@ -0,0 +1,11 @@
+%mode init
+A1=INT(PI()*100000)
+B1=INT(7.89)
+C1=INT(-23.45)
+%calc
+%mode result
+A1=314159
+B1=7
+C1=-24
+%check
+%exit
diff --git a/test/04-function-replace.txt b/test/04-function-replace.txt
new file mode 100644
index 0000000..d2225f5
--- /dev/null
+++ b/test/04-function-replace.txt
@@ -0,0 +1,20 @@
+%mode init
+A1@My dog is not big.
+A2=REPLACE(A1,4,3,"elephant")
+B1@家の犬は大きくない。
+B2=REPLACE(B1,3,1,"子猫")
+B3=REPLACE(B1,100,100,"でもうるさい。")
+B4=REPLACE(B1,1,0,"我が")
+B5=REPLACE(B1,-1,0,"foo")
+B6=REPLACE(B1,1,-1,"foo")
+B7=REPLACE(B1,FIND("犬",B1),1,"倉庫")
+%calc
+%mode result
+A2="My elephant is not big."
+B2="家の子猫は大きくない。"
+B3="家の犬は大きくない。でもうるさい。"
+B4="我が家の犬は大きくない。"
+B5=#VALUE!
+B6=#VALUE!
+B7="家の倉庫は大きくない。"
+%check
diff --git a/test/04-function-rept.txt b/test/04-function-rept.txt
new file mode 100644
index 0000000..031a854
--- /dev/null
+++ b/test/04-function-rept.txt
@@ -0,0 +1,19 @@
+%mode init
+A1=REPT("a", 0)
+A2=REPT("a", 1)
+A3=REPT("a", 2)
+A4=REPT("a", 3)
+A5=REPT("a", -1)
+B1=REPT("<->", 5)
+B2=REPT(RIGHT("abcdef", 3), 2)
+%calc
+%mode result
+A1=""
+A2="a"
+A3="aa"
+A4="aaa"
+A5=#VALUE!
+B1="<-><-><-><-><->"
+B2="defdef"
+%check
+
diff --git a/test/04-function-right-utf8.txt b/test/04-function-right-utf8.txt
new file mode 100644
index 0000000..b8cd8eb
--- /dev/null
+++ b/test/04-function-right-utf8.txt
@@ -0,0 +1,33 @@
+%mode init
+A1@今日は姉の誕生日です。
+B1=RIGHT(A1,0)
+B2=RIGHT(A1,1)
+B3=RIGHT(A1,2)
+B4=RIGHT(A1,3)
+B5=RIGHT(A1,4)
+B6=RIGHT(A1,5)
+B7=RIGHT(A1,6)
+B8=RIGHT(A1,7)
+B9=RIGHT(A1,8)
+B10=RIGHT(A1,9)
+B11=RIGHT(A1,10)
+B12=RIGHT(A1,11)
+B13=RIGHT(A1,12)
+B14=RIGHT(A1)
+%calc
+%mode result
+B1=""
+B2="。"
+B3="す。"
+B4="です。"
+B5="日です。"
+B6="生日です。"
+B7="誕生日です。"
+B8="の誕生日です。"
+B9="姉の誕生日です。"
+B10="は姉の誕生日です。"
+B11="日は姉の誕生日です。"
+B12="今日は姉の誕生日です。"
+B13="今日は姉の誕生日です。"
+B14="。"
+%check
diff --git a/test/04-function-right.txt b/test/04-function-right.txt
new file mode 100644
index 0000000..e12cf71
--- /dev/null
+++ b/test/04-function-right.txt
@@ -0,0 +1,45 @@
+%% Test built-in function RIGHT.
+%mode init
+A1@ABCDE
+A2:12345
+B1=RIGHT(A1)
+B2=RIGHT(A1,0)
+B3=RIGHT(A1,1)
+B4=RIGHT(A1,2)
+B5=RIGHT(A1,3)
+B6=RIGHT(A1,4)
+B7=RIGHT(A1,5)
+B8=RIGHT(A1,6)
+B9=RIGHT(B8,3)
+C1=RIGHT(A2)
+C2=RIGHT(A2,0)
+C3=RIGHT(A2,1)
+C4=RIGHT(A2,2)
+C5=RIGHT(A2,3)
+C6=RIGHT(A2,4)
+C7=RIGHT(A2,5)
+C8=RIGHT(A2,6)
+C9=RIGHT(C8,2)
+D1=RIGHT(A1,-1)
+%calc
+%mode result
+B1="E"
+B2=""
+B3="E"
+B4="DE"
+B5="CDE"
+B6="BCDE"
+B7="ABCDE"
+B8="ABCDE"
+B9="CDE"
+C1="5"
+C2=""
+C3="5"
+C4="45"
+C5="345"
+C6="2345"
+C7="12345"
+C8="12345"
+C9="45"
+D1=#VALUE!
+%check
diff --git a/test/04-function-sheet.txt b/test/04-function-sheet.txt
new file mode 100644
index 0000000..6d49c8c
--- /dev/null
+++ b/test/04-function-sheet.txt
@@ -0,0 +1,31 @@
+%mode session
+insert-sheet:Sheet1
+insert-sheet:Sheet2
+insert-sheet:Sheet3
+current-sheet:Sheet1
+%mode init
+A1=SHEET(Sheet1!B1)
+A2=SHEET(Sheet2!B1)
+A3=SHEET(Sheet3!B1)
+B1=SHEET(Sheet1!D1:F10)
+B2=SHEET(Sheet2!D1:F10)
+B3=SHEET(Sheet3!D1:F10)
+C1=SHEET("Sheet1")
+C2=SHEET("Sheet2")
+C3=SHEET("Sheet3")
+C4=SHEET("DoesNotExist")
+D1=SHEET()
+%calc
+%mode result
+A1=1
+A2=2
+A3=3
+B1=1
+B2=2
+B3=3
+C1=1
+C2=2
+C3=3
+C4=#N/A
+D1=1
+%check
diff --git a/test/04-function-sheets.txt b/test/04-function-sheets.txt
new file mode 100644
index 0000000..c427fda
--- /dev/null
+++ b/test/04-function-sheets.txt
@@ -0,0 +1,27 @@
+%% SHEETS returns the number of sheets contained in the reference, or the total
+%% number of sheets in the whole document.
+%mode session
+insert-sheet:Sheet1
+insert-sheet:Sheet2
+insert-sheet:Sheet3
+current-sheet:Sheet1
+%mode init
+A1=SHEETS()
+A2=SHEETS(A1)
+A3=SHEETS(Sheet2!B1:C4)
+A4=SHEETS("str")
+A5=SHEETS(1.2)
+A6=SHEETS(Sheet1:Sheet2!A1)
+A7=SHEETS(Sheet2:Sheet3!B2)
+A8=SHEETS(Sheet1:Sheet3!B10)
+%calc
+%mode result
+A1=3
+A2=1
+A3=1
+A4=#N/A
+A5=#N/A
+A6=2
+A7=2
+A8=3
+%check
diff --git a/test/04-function-single.txt b/test/04-function-single.txt
new file mode 100644
index 0000000..28bb5cb
--- /dev/null
+++ b/test/04-function-single.txt
@@ -0,0 +1,14 @@
+%% Test built-in functions.
+%mode init
+A1=MAX(10,20,5,36)
+A2=MIN(10,20,5,36)
+A3=AVERAGE(10,20,30,40)
+A4=SUM(1,1,1)+SUM(4,5,6)
+%calc
+%mode result
+A1=36
+A2=5
+A3=25
+A4=18
+%check
+%exit
diff --git a/test/04-function-substitute.txt b/test/04-function-substitute.txt
new file mode 100644
index 0000000..faa8fa7
--- /dev/null
+++ b/test/04-function-substitute.txt
@@ -0,0 +1,34 @@
+%mode init
+A1=SUBSTITUTE("I like my dogs.","dog","cat")
+A2=SUBSTITUTE("dog dog dog","dog","cat", 1)
+A3=SUBSTITUTE("dog dog dog","dog","cat", 2)
+A4=SUBSTITUTE("dog dog dog","dog","cat", 3)
+A5=SUBSTITUTE("dog dog dog","dog","cat", 4)
+A6=SUBSTITUTE("dog dog dog","dog","cat")
+A7=SUBSTITUTE("dogdogdog","dog","cat")
+A8=SUBSTITUTE("Dog and dog","dog","bird")
+A9=SUBSTITUTE("t","a","b",0)
+A10=SUBSTITUTE(111101,1,8)
+A11=SUBSTITUTE(111101,1,8,3)
+A12=SUBSTITUTE("いちにいちにいちに","いち","壱")
+A13=SUBSTITUTE("いちにいちにいちに","いち","壱",1)
+A14=SUBSTITUTE("いちにいちにいちに","いち","壱",2)
+A15=SUBSTITUTE("いちにいちにいちに","いち","壱",3)
+%calc
+%mode result
+A1="I like my cats."
+A2="cat dog dog"
+A3="dog cat dog"
+A4="dog dog cat"
+A5="dog dog dog"
+A6="cat cat cat"
+A7="catcatcat"
+A8="Dog and bird"
+A9=#VALUE!
+A10="888808"
+A11="118101"
+A12="壱に壱に壱に"
+A13="壱にいちにいちに"
+A14="いちに壱にいちに"
+A15="いちにいちに壱に"
+%check
diff --git a/test/04-function-t.txt b/test/04-function-t.txt
new file mode 100644
index 0000000..05f7266
--- /dev/null
+++ b/test/04-function-t.txt
@@ -0,0 +1,74 @@
+%% Test case for T() function.
+%mode init
+A1@text value
+A2:12345
+A3:true
+A4:false
+A5=CONCATENATE("text"," in ","formula")
+A8=1/2
+A9=TRUE()
+A10=FALSE()
+B1=T(A1)
+B2=T(A2)
+B3=T(A3)
+B4=T(A4)
+B5=T(A5)
+B6=T(A6)
+B7=T("inline")
+B8=T(A8)
+B9=T(A9)
+B10=T(A10)
+C1=ISTEXT(B1)
+C2=ISTEXT(B2)
+C3=ISTEXT(B3)
+C4=ISTEXT(B4)
+C5=ISTEXT(B5)
+C6=ISTEXT(B6)
+C7=ISTEXT(B7)
+C8=ISTEXT(B8)
+C9=ISTEXT(B9)
+C10=ISTEXT(B10)
+D1=LEN(B1)
+D2=LEN(B2)
+D3=LEN(B3)
+D4=LEN(B4)
+D5=LEN(B5)
+D6=LEN(B6)
+D7=LEN(B7)
+D8=LEN(B8)
+D9=LEN(B9)
+D10=LEN(B10)
+%calc
+%mode result
+B1="text value"
+B2=""
+B3=""
+B4=""
+B5="text in formula"
+B6=""
+B7="inline"
+B8=""
+B9=""
+B10=""
+C1=true
+C2=true
+C3=true
+C4=true
+C5=true
+C6=true
+C7=true
+C8=true
+C9=true
+C10=true
+D1=10
+D2=0
+D3=0
+D4=0
+D5=15
+D6=0
+D7=6
+D8=0
+D9=0
+D10=0
+%check
+
diff --git a/test/04-function-textjoin.txt b/test/04-function-textjoin.txt
new file mode 100644
index 0000000..ff9b73a
--- /dev/null
+++ b/test/04-function-textjoin.txt
@@ -0,0 +1,30 @@
+%mode init
+A1@A
+A2@C
+A3:2.3
+A4:true
+A5=21/3
+B1@B
+B2="D"
+B10=REPT("Z",2)
+C1=TEXTJOIN(", ",0,A1:B2)
+C2=TEXTJOIN(", ",0,A1:A2,B1:B2)
+C3=TEXTJOIN(", ",0,A1:B1,A2:B2)
+C4=TEXTJOIN(":",0,A1,B2,A2,B1,A1)
+C5=TEXTJOIN(":",0,A1:A4)
+C6=TEXTJOIN(",",0,B1:B10)
+C7=TEXTJOIN(",",1,B1:B10)
+C8=TEXTJOIN("@",1,A1:A5)
+%calc
+%mode result
+C1="A, B, C, D"
+C2="A, C, B, D"
+C3="A, B, C, D"
+C4="A:D:C:B:A"
+C5="A:C:2.3:true"
+C6="B,D,,,,,,,,ZZ"
+C7="B,D,ZZ"
+C8="A@C@2.3@true@7"
+%check
+
+
diff --git a/test/04-function-trim.txt b/test/04-function-trim.txt
new file mode 100644
index 0000000..7c47729
--- /dev/null
+++ b/test/04-function-trim.txt
@@ -0,0 +1,21 @@
+%mode init
+A1=TRIM("one two three")
+A2=TRIM(" one two three")
+A3=TRIM(" one two three ")
+A4=TRIM("A")
+A5=TRIM("A B")
+A6=TRIM("")
+A7=TRIM(12345)
+B1@ 日本 韓国 中国
+B2=TRIM(B1)
+%calc
+%mode result
+A1="one two three"
+A2="one two three"
+A3="one two three"
+A4="A"
+A5="A B"
+A6=""
+A7="12345"
+B2="日本 韓国 中国"
+%check
diff --git a/test/04-function-true-false.txt b/test/04-function-true-false.txt
new file mode 100644
index 0000000..71b7933
--- /dev/null
+++ b/test/04-function-true-false.txt
@@ -0,0 +1,34 @@
+%mode init
+A1=TRUE()
+A2=FALSE()
+A3@string value
+A4=A3
+A5:true
+A6:false
+B1=NOT(A1)
+B2=NOT(A2)
+B3=NOT(1)
+B4=NOT(0)
+B5=NOT(-1)
+B6=NOT("str")
+B7=NOT(A3)
+B8=NOT(A4)
+B9=NOT(A5)
+B10=NOT(A6)
+B11=NOT(A7)
+%calc
+%mode result
+A1=true
+A2=false
+B1=false
+B2=true
+B3=false
+B4=true
+B5=false
+B6=#VALUE!
+B7=#VALUE!
+B8=#VALUE!
+B9=false
+B10=true
+B11=true
+%check
diff --git a/test/04-function-type.txt b/test/04-function-type.txt
new file mode 100644
index 0000000..aaabdb2
--- /dev/null
+++ b/test/04-function-type.txt
@@ -0,0 +1,42 @@
+%mode init
+A1=TYPE(1)
+A2=TYPE("str")
+A3=TYPE(TRUE())
+A4=TYPE(FALSE())
+A5=TYPE(NA())
+A6=TYPE(B1:E2)
+A7=TYPE(MMULT(D1:D3,E1:G1))
+B1@string value
+B2:12
+B3:true
+B4:false
+B5=1/2
+B6=NA()
+B7="str"
+B8=ISEVEN(12)
+C1=TYPE(B1)
+C2=TYPE(B2)
+C3=TYPE(B3)
+C4=TYPE(B4)
+C5=TYPE(B5)
+C6=TYPE(B6)
+C7=TYPE(B7)
+C8=TYPE(B8)
+%calc
+%mode result
+A1=1
+A2=2
+A3=4
+A4=4
+A5=16
+A6=64
+A7=64
+C1=2
+C2=1
+C3=4
+C4=4
+C5=1
+C6=16
+C7=2
+C8=4
+%check
diff --git a/test/05-range-reference.txt b/test/05-range-reference.txt
new file mode 100644
index 0000000..25a240a
--- /dev/null
+++ b/test/05-range-reference.txt
@@ -0,0 +1,25 @@
+%mode init
+A1=SUM(B1:B4)
+A2=SUM(C1:C4)
+B1=1
+B3=5
+C2=10
+C4=100
+%calc
+%mode result
+A1=6
+A2=110
+%check
+%mode edit
+B1=6
+%recalc
+%mode result
+A1=11
+%check
+%mode edit
+C4=1000
+%recalc
+%mode result
+A2=1010
+%check
+%exit
diff --git a/test/06-range-reference-basic-01.txt b/test/06-range-reference-basic-01.txt
new file mode 100644
index 0000000..d08ebc7
--- /dev/null
+++ b/test/06-range-reference-basic-01.txt
@@ -0,0 +1,28 @@
+%% Very simple case of range reference tracking.
+%mode init
+B1=1
+B3=5
+D1=SUM(B1:B4)
+%calc
+%mode result
+D1=6
+%check
+%mode edit
+B2=4
+%recalc
+%mode result
+D1=10
+%check
+%mode edit
+B1=10
+%recalc
+%mode result
+D1=19
+%check
+%mode edit
+B4=1
+%recalc
+%mode result
+D1=20
+%check
+%exit
diff --git a/test/06-range-reference-basic-02.txt b/test/06-range-reference-basic-02.txt
new file mode 100644
index 0000000..b97c2df
--- /dev/null
+++ b/test/06-range-reference-basic-02.txt
@@ -0,0 +1,34 @@
+%% Chained range reference tracking.
+%mode init
+A1=SUM(A2:A3)
+A3=SUM(A4:A5)
+A5=5
+%calc
+%mode result
+A1=5
+A3=5
+A5=5
+%check
+%mode edit
+A5=1
+%recalc
+%mode result
+A1=1
+A3=1
+%check
+%mode edit
+A4=10
+%recalc
+%mode result
+A3=11
+A1=11
+%check
+%mode edit
+A2=20
+%recalc
+%mode result
+A1=31
+A3=11
+%check
+%exit
+
diff --git a/test/06-range-reference-circular-01.txt b/test/06-range-reference-circular-01.txt
new file mode 100644
index 0000000..b9af839
--- /dev/null
+++ b/test/06-range-reference-circular-01.txt
@@ -0,0 +1,11 @@
+%% Test for circular range references.
+%mode init
+A1=SUM(B1:B2)
+%calc
+%mode result
+A1=0
+%check
+%mode edit
+B1=SUM(A1:A2)
+%recalc
+%exit
diff --git a/test/06-range-reference-unordered.txt b/test/06-range-reference-unordered.txt
new file mode 100644
index 0000000..34a4cd7
--- /dev/null
+++ b/test/06-range-reference-unordered.txt
@@ -0,0 +1,17 @@
+%% Test case for handling disordered range reference.
+%mode init
+A1:1
+A2:2
+A3:3
+A4=SUM(A3:A1)
+%calc
+%mode result
+A4=6
+%check
+%mode edit
+A2:4
+%recalc
+%mode result
+A4=8
+%check
+
diff --git a/test/06-range-reference-whole-column.txt b/test/06-range-reference-whole-column.txt
new file mode 100644
index 0000000..d806bc6
--- /dev/null
+++ b/test/06-range-reference-whole-column.txt
@@ -0,0 +1,17 @@
+%% Test case for handling a range reference with whole column.
+%mode init
+A1:1
+A2:2
+A3:3
+C1=SUM(A:A)
+%calc
+%mode result
+C1=6
+%check
+%mode edit
+A1:10
+%recalc
+%mode result
+C1=15
+%check
+%exit
diff --git a/test/06-range-reference-whole-row.txt b/test/06-range-reference-whole-row.txt
new file mode 100644
index 0000000..10a38a0
--- /dev/null
+++ b/test/06-range-reference-whole-row.txt
@@ -0,0 +1,17 @@
+%% Test case for handling a range reference with whole row.
+%mode init
+A2:1
+B2:2
+C2:3
+E3=SUM(2:2)
+%calc
+%mode result
+E3=6
+%check
+%mode edit
+A2:10
+%recalc
+%mode result
+E3=15
+%check
+%exit
diff --git a/test/07-fraction-numbers.txt b/test/07-fraction-numbers.txt
new file mode 100644
index 0000000..852edba
--- /dev/null
+++ b/test/07-fraction-numbers.txt
@@ -0,0 +1,12 @@
+%% Test parsing and interpretation of fractional numbers.
+%mode init
+A1=25.6+10.56-0.987
+A2=2.5+3.2
+A3=3.5*(2.3+7.567)
+%calc
+%mode result
+A1=35.173
+A2=5.7
+A3=34.5345
+%check
+%exit
diff --git a/test/08-boolean-cells.txt b/test/08-boolean-cells.txt
new file mode 100644
index 0000000..d10caf5
--- /dev/null
+++ b/test/08-boolean-cells.txt
@@ -0,0 +1,25 @@
+%% Test for parsing boolean cells.
+%% --------------------------------------------------------------------------
+%mode init
+A1@Boolean Values
+A2:true
+A3:false
+A4=A2*2
+A5=A3+3
+A7=COUNTA(A1:B3)
+A8=SUM(A1:A5)
+%% --------------------------------------------------------------------------
+%calc
+%% --------------------------------------------------------------------------
+%mode result
+A1="Boolean Values"
+A2=true
+A3=false
+A4=2
+A5=3
+A7=3
+A8=6
+%% --------------------------------------------------------------------------
+%check
+%% --------------------------------------------------------------------------
+%exit
diff --git a/test/08-numeric-cells.txt b/test/08-numeric-cells.txt
new file mode 100644
index 0000000..d324d04
--- /dev/null
+++ b/test/08-numeric-cells.txt
@@ -0,0 +1,17 @@
+%% Test for parsing numeric cells. A numeric cell begins with a cell name
+%% followed by a ':' immediately followed by a number.
+%mode init
+A1:0
+A2:1
+A3:12.5
+A4:-1.2
+A5=SUM(A1:A4)
+%calc
+%mode result
+A1=0
+A2=1
+A3=12.5
+A4=-1.2
+A5=12.3
+%check
+%exit
diff --git a/test/09-string-cells.txt b/test/09-string-cells.txt
new file mode 100644
index 0000000..e841c37
--- /dev/null
+++ b/test/09-string-cells.txt
@@ -0,0 +1,17 @@
+%% Test for parsing string cells.
+%mode init
+A1@Andy
+B1@Bruce
+C1@Charles
+D1@David
+A2=A1
+%calc
+%mode result
+A1="Andy"
+B1="Bruce"
+C1="Charles"
+D1="David"
+A2="Andy"
+%check
+%exit
+
diff --git a/test/10-shared-formulas-01.txt b/test/10-shared-formulas-01.txt
new file mode 100644
index 0000000..751790c
--- /dev/null
+++ b/test/10-shared-formulas-01.txt
@@ -0,0 +1,9 @@
+%% Test for simple columnar shared formulas.
+%mode init
+A1:100
+A2=A1+1
+A3=A2+1
+A4=A3+1
+A5=A4+1
+%calc
+%exit
diff --git a/test/11-reference-to-numeric-cell-01.txt b/test/11-reference-to-numeric-cell-01.txt
new file mode 100644
index 0000000..bf333f4
--- /dev/null
+++ b/test/11-reference-to-numeric-cell-01.txt
@@ -0,0 +1,23 @@
+%% Modify non-formula cell which should trigger formula cells referencing it
+%% to get re-calculated.
+%mode init
+A1:1
+A2=A1*A1
+A3=A1*3
+B1=A1*3+10
+%calc
+%mode result
+A2=1
+A3=3
+B1=13
+%check
+%mode edit
+A1:2
+%recalc
+%mode result
+A2=4
+A3=6
+B1=16
+%check
+%exit
+
diff --git a/test/12-inline-string-01.txt b/test/12-inline-string-01.txt
new file mode 100644
index 0000000..39fdafd
--- /dev/null
+++ b/test/12-inline-string-01.txt
@@ -0,0 +1,33 @@
+%% Test for inline string support.
+%mode init
+A1="A"
+A2="n"
+A3="d"
+A4="y"
+A5=A1
+A6=LEN("test")
+A7=LEN(123)
+A8=LEN(123.45)
+A9="Andy"
+A10=A9
+A11=LEN(A9)
+A12=CONCATENATE(A1,A2,A3,A4)
+A13=CONCATENATE(A12," is smart")
+%calc
+%mode result
+A1="A"
+A2="n"
+A3="d"
+A4="y"
+A5="A"
+A6=4
+A7=3
+A8=6
+A9="Andy"
+A10="Andy"
+A11=4
+A12="Andy"
+A13="Andy is smart"
+%check
+%exit
+
diff --git a/test/13-relational-operators-01.txt b/test/13-relational-operators-01.txt
new file mode 100644
index 0000000..8ce4434
--- /dev/null
+++ b/test/13-relational-operators-01.txt
@@ -0,0 +1,30 @@
+%% Test for relational operators with in-line numbers.
+%mode init
+A1=1=1
+A2=1=2
+A3=1*6=2*3
+A4=1<4
+A5=4<4
+A6=4>2
+A7=68>123
+A8=1>=1
+A9=1>=2
+A10=2>=0
+A11=45<>12
+A12=33<>33
+%calc
+%mode result
+A1=1
+A2=0
+A3=1
+A4=1
+A5=0
+A6=1
+A7=0
+A8=1
+A9=0
+A10=1
+A11=1
+A12=0
+%check
+%exit
diff --git a/test/13-relational-operators-02.txt b/test/13-relational-operators-02.txt
new file mode 100644
index 0000000..a9ab67e
--- /dev/null
+++ b/test/13-relational-operators-02.txt
@@ -0,0 +1,23 @@
+%% Test for relational operators with referenced numbers.
+%mode init
+A1:1
+A2:2
+A3:3
+A4=A1<A2
+A5=A1>A2
+A6=A1<>A2
+A7=A1>=A2
+A8=A1<=A2
+A9=(A1+A2)=A3
+A10=A1=A2
+%calc
+%mode result
+A4=1
+A5=0
+A6=1
+A7=0
+A8=1
+A9=1
+A10=0
+%check
+%exit
diff --git a/test/13-relational-operators-03.txt b/test/13-relational-operators-03.txt
new file mode 100644
index 0000000..6cebb24
--- /dev/null
+++ b/test/13-relational-operators-03.txt
@@ -0,0 +1,19 @@
+%% Test for relational operators with strings.
+%mode init
+A1@A
+A2@B
+A3@C
+B1=A1=A1
+B2=A1>=A1
+B3=A2<=A2
+B4=A1>A2
+B5=A1<A2
+%calc
+%mode result
+B1=1
+B2=1
+B3=1
+B4=0
+B5=1
+%check
+%exit
diff --git a/test/20-table-reference-01.txt b/test/20-table-reference-01.txt
new file mode 100644
index 0000000..0e99185
--- /dev/null
+++ b/test/20-table-reference-01.txt
@@ -0,0 +1,37 @@
+%% Basic table reference test. Set C3:D9 to be a table range with 2 columns
+%% 'Category' and 'Value'.
+%mode init
+C3@Category
+C4@A
+C5@B
+C6@C
+C7@D
+C8@E
+C9@Total
+D3@Value
+D4:1
+D5:2
+D6:3
+D7:4
+D8:5
+D9=SUBTOTAL(109,[Value])
+E1=SUM(Table1[Value])
+E2=COUNTA(Table1[#Headers])
+E3=COUNTA(Table1[[#Headers],[Category]])
+E4=COUNTA(Table1[[#Headers],[#Data],[Category]])
+%mode table
+name=Table1
+range=C3:D9
+columns=Category,Value
+totals-row-count=1
+%push
+%calc
+%mode result
+D9=15
+E1=15
+E2=2
+E3=1
+E4=6
+%check
+%exit
+
diff --git a/test/21-named-exp-workbook-01.txt b/test/21-named-exp-workbook-01.txt
new file mode 100644
index 0000000..ee55057
--- /dev/null
+++ b/test/21-named-exp-workbook-01.txt
@@ -0,0 +1,54 @@
+%% Basic named expression support.
+%% --------------------------------------------------------------------------
+%mode session
+row-limit:1000
+column-limit:100
+insert-sheet:Sheet1
+insert-sheet:Sheet2
+current-sheet:Sheet1
+%% --------------------------------------------------------------------------
+%mode init
+A1:1
+A2:2
+A3:3
+A4:4
+A5:5
+B1:6
+B2:7
+B3:8
+B4:9
+B5:10
+A7=SUM(MyRange)
+B7=SUM(MyRange2)
+%% --------------------------------------------------------------------------
+%mode named-expression
+name=MyRange
+expression=Sheet1!$A$1:$A$5
+origin=Sheet1!$A$1
+%push
+%% --------------------------------------------------------------------------
+%mode named-expression
+name=MyRange2
+expression=Sheet1!$A$1:$B$5
+origin=Sheet1!$A$1
+%push
+%% --------------------------------------------------------------------------
+%calc
+%% --------------------------------------------------------------------------
+%mode result
+A7=15
+B7=55
+%check
+%% --------------------------------------------------------------------------
+%mode edit
+A1:10
+%% --------------------------------------------------------------------------
+%recalc
+%% --------------------------------------------------------------------------
+%mode result
+A7=24
+B7=64
+%check
+%% --------------------------------------------------------------------------
+%exit
+
diff --git a/test/21-named-exp-worksheet-01.txt b/test/21-named-exp-worksheet-01.txt
new file mode 100644
index 0000000..0356c54
--- /dev/null
+++ b/test/21-named-exp-worksheet-01.txt
@@ -0,0 +1,73 @@
+%% Named expression in sheet scope.
+%% Sizes of the equally named sheet-local ranges are intentionally different,
+%% to easily test whether the correct range is picked on each sheet.
+%% --------------------------------------------------------------------------
+%mode session
+row-limit:1000
+column-limit:100
+insert-sheet:Sheet1
+insert-sheet:Sheet2
+current-sheet:Sheet1
+display-sheet-name:true
+%% --------------------------------------------------------------------------
+%mode init
+A1:1
+A2:2
+A3:3
+A4:4
+A5:5
+B1:6
+B2:7
+B3:8
+B4:9
+B5:10
+B7=SUM(MyLocalRange)
+%% --------------------------------------------------------------------------
+%mode session
+current-sheet:Sheet2
+%% --------------------------------------------------------------------------
+%mode init
+A1:10
+A2:20
+A3:30
+A4:40
+A5:50
+B1:60
+B2:70
+B3:80
+B4:90
+B5:100
+B7=SUM(MyLocalRange)
+%% --------------------------------------------------------------------------
+%mode named-expression
+name=MyLocalRange
+scope=Sheet1
+expression=Sheet1!$A$1:$A$5
+origin=Sheet1!$A$1
+%push
+%% --------------------------------------------------------------------------
+%mode named-expression
+name=MyLocalRange
+scope=Sheet2
+expression=Sheet2!$A$4:$B$5
+origin=Sheet2!$A$1
+%push
+%% --------------------------------------------------------------------------
+%calc
+%% --------------------------------------------------------------------------
+%mode result
+Sheet1!B7=15
+Sheet2!B7=280
+%check
+%% --------------------------------------------------------------------------
+%mode edit
+Sheet1!A5@some text
+Sheet2!B4@other text
+%recalc
+%% --------------------------------------------------------------------------
+%mode result
+Sheet1!B7=10
+Sheet2!B7=190
+%check
+%exit
+
diff --git a/test/22-formulas-with-cached-results.txt b/test/22-formulas-with-cached-results.txt
new file mode 100644
index 0000000..14638b9
--- /dev/null
+++ b/test/22-formulas-with-cached-results.txt
@@ -0,0 +1,15 @@
+%% Test case for formula cells with cached results. This test case intentionally
+%% does not calculate formula cells before checking their results.
+%mode init
+D2=RAND()
+E6=RAND()+12
+F7=145/RAND()
+%mode result-cache
+D2=12
+E6=145
+F7=#DIV/0!
+%mode result
+D2=12
+E6=145
+F7=#DIV/0!
+%check
diff --git a/test/22-grouped-formulas-with-cached-results.txt b/test/22-grouped-formulas-with-cached-results.txt
new file mode 100644
index 0000000..cbaf33b
--- /dev/null
+++ b/test/22-grouped-formulas-with-cached-results.txt
@@ -0,0 +1,38 @@
+%mode init
+A1=RAND()
+A2=RAND()
+B1=RAND()
+B2=RAND()
+D1=RAND()
+D2=RAND()
+E1=RAND()
+E2=RAND()
+{G1:H2}{=MMULT(A1:B2,D1:E2)}
+%mode result-cache
+A1=1
+A2=2
+B1=3
+B2=4
+D1=5
+D2=6
+E1=7
+E2=8
+G1=23
+G2=34
+H1=31
+H2=46
+%mode result
+A1=1
+A2=2
+B1=3
+B2=4
+D1=5
+D2=6
+E1=7
+E2=8
+G1=23
+G2=34
+H1=31
+H2=46
+%check
+%exit
diff --git a/test/parser-test-func.sh b/test/parser-test-func.sh
new file mode 100755
index 0000000..f55c41e
--- /dev/null
+++ b/test/parser-test-func.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+exec_test()
+{
+ PROGDIR=`dirname $0`
+ SRCDIR=$PROGDIR/../src
+
+ export PATH=$SRCDIR:$SRCDIR/.libs:$PATH
+
+ ixion-parser -t $1 $PROGDIR/*.txt
+}
+
diff --git a/test/parser-test-t0.sh b/test/parser-test-t0.sh
new file mode 100755
index 0000000..6f6c727
--- /dev/null
+++ b/test/parser-test-t0.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+PROGDIR=`dirname $0`
+source $PROGDIR/parser-test-func.sh
+
+THREAD=`echo $0 | sed -e 's/.*t\([0-9]\)\.sh/\1/g'`
+
+exec_test $THREAD
+
diff --git a/test/parser-test-t1.sh b/test/parser-test-t1.sh
new file mode 100755
index 0000000..6f6c727
--- /dev/null
+++ b/test/parser-test-t1.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+PROGDIR=`dirname $0`
+source $PROGDIR/parser-test-func.sh
+
+THREAD=`echo $0 | sed -e 's/.*t\([0-9]\)\.sh/\1/g'`
+
+exec_test $THREAD
+
diff --git a/test/parser-test-t2.sh b/test/parser-test-t2.sh
new file mode 100755
index 0000000..6f6c727
--- /dev/null
+++ b/test/parser-test-t2.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+PROGDIR=`dirname $0`
+source $PROGDIR/parser-test-func.sh
+
+THREAD=`echo $0 | sed -e 's/.*t\([0-9]\)\.sh/\1/g'`
+
+exec_test $THREAD
+
diff --git a/test/parser-test-t3.sh b/test/parser-test-t3.sh
new file mode 100755
index 0000000..6f6c727
--- /dev/null
+++ b/test/parser-test-t3.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+PROGDIR=`dirname $0`
+source $PROGDIR/parser-test-func.sh
+
+THREAD=`echo $0 | sed -e 's/.*t\([0-9]\)\.sh/\1/g'`
+
+exec_test $THREAD
+
diff --git a/test/parser-test-t4.sh b/test/parser-test-t4.sh
new file mode 100755
index 0000000..6f6c727
--- /dev/null
+++ b/test/parser-test-t4.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+PROGDIR=`dirname $0`
+source $PROGDIR/parser-test-func.sh
+
+THREAD=`echo $0 | sed -e 's/.*t\([0-9]\)\.sh/\1/g'`
+
+exec_test $THREAD
+
diff --git a/test/parser-test-t5.sh b/test/parser-test-t5.sh
new file mode 100755
index 0000000..6f6c727
--- /dev/null
+++ b/test/parser-test-t5.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+PROGDIR=`dirname $0`
+source $PROGDIR/parser-test-func.sh
+
+THREAD=`echo $0 | sed -e 's/.*t\([0-9]\)\.sh/\1/g'`
+
+exec_test $THREAD
+
diff --git a/test/parser-test-t6.sh b/test/parser-test-t6.sh
new file mode 100755
index 0000000..6f6c727
--- /dev/null
+++ b/test/parser-test-t6.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+PROGDIR=`dirname $0`
+source $PROGDIR/parser-test-func.sh
+
+THREAD=`echo $0 | sed -e 's/.*t\([0-9]\)\.sh/\1/g'`
+
+exec_test $THREAD
+
diff --git a/test/parser-test-t7.sh b/test/parser-test-t7.sh
new file mode 100755
index 0000000..6f6c727
--- /dev/null
+++ b/test/parser-test-t7.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+PROGDIR=`dirname $0`
+source $PROGDIR/parser-test-func.sh
+
+THREAD=`echo $0 | sed -e 's/.*t\([0-9]\)\.sh/\1/g'`
+
+exec_test $THREAD
+
diff --git a/test/parser-test-t8.sh b/test/parser-test-t8.sh
new file mode 100755
index 0000000..6f6c727
--- /dev/null
+++ b/test/parser-test-t8.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+PROGDIR=`dirname $0`
+source $PROGDIR/parser-test-func.sh
+
+THREAD=`echo $0 | sed -e 's/.*t\([0-9]\)\.sh/\1/g'`
+
+exec_test $THREAD
+
diff --git a/test/python/document.py b/test/python/document.py
new file mode 100755
index 0000000..538225b
--- /dev/null
+++ b/test/python/document.py
@@ -0,0 +1,242 @@
+#!/usr/bin/env python3
+
+import unittest
+import ixion
+
+
+class DocumentTest(unittest.TestCase):
+
+ def setUp(self):
+ self.doc = ixion.Document()
+
+ def test_append_sheets(self):
+ tests = (
+ "Normal", # normal name
+ "First Sheet", # white space
+ "Laura's", # single quote
+ '"Quoted"' # double quote
+ )
+
+ sheets = []
+ for test in tests:
+ sh = self.doc.append_sheet(test)
+ sheets.append(sh)
+
+ for test, sheet in zip(tests, sheets):
+ self.assertEqual(test, sheet.name)
+
+ self.assertEqual(tests, self.doc.sheet_names)
+
+ for i, test in enumerate(tests):
+ # get sheet by index.
+ sh = self.doc.get_sheet(i)
+ self.assertEqual(test, sh.name)
+
+ for test in tests:
+ # get sheet by name.
+ sh = self.doc.get_sheet(test)
+ self.assertEqual(test, sh.name)
+
+ try:
+ sheets[0].name = "Try to change sheet name"
+ self.assertTrue(False, "sheet name attribute should not be writable.")
+ except AttributeError:
+ pass # AttributeError is expected when attempting to overwrite sheet name attribute.
+ except:
+ self.assertTrue(False, "Wrong exception has been raised")
+
+ # Trying to insert a new sheet with an existing name should fail.
+ try:
+ sh = self.doc.append_sheet(tests[0])
+ self.assertTrue(False, "Trying to insert a new sheet with an existing sheet name should fail")
+ except ixion.DocumentError:
+ # This is expected.
+ pass
+
+ def test_numeric_cell_input(self):
+ sh1 = self.doc.append_sheet("Data")
+
+ # Empty cell should yield a value of 0.0.
+ check_val = sh1.get_numeric_value(0, 0)
+ self.assertEqual(0.0, check_val)
+
+ tests = (
+ # row, column, value
+ (3, 1, 11.2),
+ (4, 1, 12.0),
+ (6, 2, -12.0),
+ (6, 3, 0.0)
+ )
+
+ for test in tests:
+ sh1.set_numeric_cell(test[0], test[1], test[2]) # row, column, value
+ check_val = sh1.get_numeric_value(column=test[1], row=test[0]) # swap row and column
+ self.assertEqual(test[2], check_val)
+
+ def test_string_cell_input(self):
+ sh1 = self.doc.append_sheet("Data")
+
+ # Empty cell should yield an empty string.
+ check_val = sh1.get_string_value(0, 0)
+ self.assertEqual("", check_val)
+
+ tests = (
+ # row, column, value
+ (0, 0, "normal string"), # normal string
+ (1, 0, "A1+B1"), # string that looks like a formula expression
+ (2, 0, "'single quote'"), # single quote
+ (3, 0, "80's music"), # single quote
+ (4, 0, '"The" Music in the 80\'s'), # single and double quotes mixed
+ )
+
+ for test in tests:
+ sh1.set_string_cell(test[0], test[1], test[2]) # row, column, value
+ check_val = sh1.get_string_value(column=test[1], row=test[0]) # swap row and column
+ self.assertEqual(test[2], check_val)
+
+ def test_formula_cell_input(self):
+ sh1 = self.doc.append_sheet("Data")
+ sh1.set_formula_cell(0, 0, "12*3")
+ try:
+ val = sh1.get_numeric_value(0, 0)
+ self.assertTrue(False, "TypeError should have been raised")
+ except TypeError:
+ # TypeError is expected when trying to fetch numeric value from
+ # formula cell before it is calculated.
+ pass
+
+ self.doc.calculate()
+ val = sh1.get_numeric_value(0, 0)
+ self.assertEqual(12*3, val)
+
+ def test_formula_cell_recalc(self):
+ sh1 = self.doc.append_sheet("Data")
+ sh1.set_numeric_cell(0, 0, 1.0)
+ sh1.set_numeric_cell(1, 0, 2.0)
+ sh1.set_numeric_cell(2, 0, 4.0)
+ sh1.set_formula_cell(3, 0, "SUM(A1:A3)")
+
+ # initial calculation
+ self.doc.calculate()
+ val = sh1.get_numeric_value(3, 0)
+ self.assertEqual(7.0, val)
+
+ # recalculation
+ sh1.set_numeric_cell(1, 0, 8.0)
+ self.doc.calculate()
+ val = sh1.get_numeric_value(3, 0)
+ self.assertEqual(13.0, val)
+
+ # add another formula cell and recalc.
+ sh1.set_formula_cell(0, 1, "A1+15")
+ sh1.set_numeric_cell(0, 0, 0.0)
+ self.doc.calculate()
+ val = sh1.get_numeric_value(0, 1)
+ self.assertEqual(15.0, val)
+ val = sh1.get_numeric_value(3, 0)
+ self.assertEqual(12.0, val)
+
+ def test_formula_cell_recalc2(self):
+ sh1 = self.doc.append_sheet("Data")
+ sh1.set_numeric_cell(4, 1, 12.0) # B5
+ sh1.set_formula_cell(5, 1, "B5*2")
+ sh1.set_formula_cell(6, 1, "B6+10")
+
+ self.doc.calculate()
+ val = sh1.get_numeric_value(4, 1)
+ self.assertEqual(12.0, val)
+ val = sh1.get_numeric_value(5, 1)
+ self.assertEqual(24.0, val)
+ val = sh1.get_numeric_value(6, 1)
+ self.assertEqual(34.0, val)
+
+ # Delete B5 and check.
+ sh1.empty_cell(4, 1)
+ self.doc.calculate()
+ val = sh1.get_numeric_value(4, 1)
+ self.assertEqual(0.0, val)
+ val = sh1.get_numeric_value(5, 1)
+ self.assertEqual(0.0, val)
+ val = sh1.get_numeric_value(6, 1)
+ self.assertEqual(10.0, val)
+
+ def test_formula_cell_threaded_recalc(self):
+
+ sh1 = self.doc.append_sheet("Data")
+ sh1.set_numeric_cell(0, 0, 1.1)
+ sh1.set_numeric_cell(1, 0, 1.2)
+ sh1.set_numeric_cell(2, 0, 1.3)
+ sh1.set_formula_cell(0, 1, "SUM(A1:A3)")
+ sh1.set_formula_cell(1, 1, "B1*2")
+
+ self.doc.calculate(threads=2)
+
+ # Check the value in B1.
+ v = sh1.get_numeric_value(0, 1)
+ v = round(v, 1)
+ self.assertEqual(v, 3.6)
+
+ # Check the value in B2.
+ v = sh1.get_numeric_value(1, 1)
+ v = round(v, 1)
+ self.assertEqual(v, 7.2)
+
+ def test_formula_cell_string(self):
+ sh1 = self.doc.append_sheet("MyData")
+ sh1.set_string_cell(1, 1, "My precious string") # B2
+ sh1.set_formula_cell(1, 2, "B2") # C2
+ sh1.set_formula_cell(2, 2, "concatenate(B2, \" is here\")") # C3
+ self.doc.calculate()
+ self.assertEqual("My precious string", sh1.get_string_value(1, 1))
+ self.assertEqual("My precious string", sh1.get_string_value(1, 2))
+ self.assertEqual("My precious string is here", sh1.get_string_value(2, 2))
+
+ def test_detached_sheet(self):
+ # You can't set values to a detached sheet that doesn't belong to a
+ # Document object.
+ sh = ixion.Sheet()
+ try:
+ sh.set_numeric_cell(1, 1, 12)
+ self.assertTrue(False, "failed to raise a SheetError.")
+ except ixion.SheetError:
+ pass # expected
+
+ try:
+ sh.set_string_cell(2, 2, "String")
+ self.assertTrue(False, "failed to raise a SheetError.")
+ except ixion.SheetError:
+ pass # expected
+
+ try:
+ sh.set_formula_cell(2, 2, "A1")
+ self.assertTrue(False, "failed to raise a SheetError.")
+ except ixion.SheetError:
+ pass # expected
+
+ try:
+ sh.empty_cell(2, 1)
+ self.assertTrue(False, "failed to raise a SheetError.")
+ except ixion.SheetError:
+ pass # expected
+
+ try:
+ val = sh.get_numeric_value(2, 1)
+ self.assertTrue(False, "failed to raise a SheetError.")
+ except ixion.SheetError:
+ pass # expected
+
+ try:
+ s = sh.get_string_value(2, 1)
+ self.assertTrue(False, "failed to raise a SheetError.")
+ except ixion.SheetError:
+ pass # expected
+
+ try:
+ expr = sh.get_formula_expression(2, 1)
+ self.assertTrue(False, "failed to raise a SheetError.")
+ except ixion.SheetError:
+ pass # expected
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/test/python/module.py b/test/python/module.py
new file mode 100755
index 0000000..88682c6
--- /dev/null
+++ b/test/python/module.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python3
+
+import unittest
+import ixion
+
+
+class ModuleTest(unittest.TestCase):
+
+ def test_column_label(self):
+ # Get a single label.
+ labels = ixion.column_label(0, 1)
+ self.assertEqual(1, len(labels))
+ self.assertEqual('A', labels[0])
+
+ # Get multiple labels.
+ labels = ixion.column_label(2, 10)
+ self.assertEqual(8, len(labels))
+ self.assertEqual(labels, ('C','D','E','F','G','H','I','J'))
+
+ # The following start, stop combos should individually raise IndexError.
+ tests = (
+ (2, 2),
+ (2, 0),
+ (-1, 10)
+ )
+ for test in tests:
+ with self.assertRaises(IndexError):
+ labels = ixion.column_label(test[0], test[1])
+
+ # Keyword arguments should work.
+ labels = ixion.column_label(start=2, stop=4)
+ self.assertEqual(labels, ('C','D'))
+
+ # Get labels in R1C1.
+ labels = ixion.column_label(5, 10, 2)
+ self.assertEqual(labels, ('6','7','8','9','10'))
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/test/thread/function-parallel.txt b/test/thread/function-parallel.txt
new file mode 100644
index 0000000..49cceea
--- /dev/null
+++ b/test/thread/function-parallel.txt
@@ -0,0 +1,11 @@
+%mode init
+A1=WAIT()+1
+A2=WAIT()+20
+A3=WAIT()+15
+A4=WAIT()+5
+A5=A1+A2+A3+A4+WAIT()
+A6=(A2+A3)*2+WAIT()
+A7=A5+A6+WAIT()
+%calc
+%exit
+
diff --git a/test/thread/function-wait-simple.txt b/test/thread/function-wait-simple.txt
new file mode 100644
index 0000000..b3df0d7
--- /dev/null
+++ b/test/thread/function-wait-simple.txt
@@ -0,0 +1,8 @@
+%mode init
+A1=WAIT()+1
+A2=WAIT()+20
+A3=WAIT()+15
+A4=(A2+A3)*2+WAIT()
+%calc
+%exit
+