GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
test-exp-parser.c
1 #include "config.h"
2 #include <glib.h>
3 #include <stdlib.h>
4 #include <stdio.h>
5 
6 #include <libguile.h>
7 #include "gnc-exp-parser.h"
8 #include "gnc-numeric.h"
9 #include "test-stuff.h"
10 #include <unittest-support.h>
11 
12 static GList *tests = NULL;
13 
14 typedef struct
15 {
16  const char * test_name;
17  const char * exp;
18  gboolean should_succeed;
19  gnc_numeric expected_result;
20  int expected_error_offset;
21  const char * file;
22  int line;
23 } TestNode;
24 
25 #define add_pass_test(n, e, r) _add_pass_test((n), (e), (r), __FILE__, __LINE__)
26 
27 static void
28 _add_pass_test (const char *test_name, const char *exp, gnc_numeric result, char *file, int line)
29 {
30  TestNode *node = g_new0 (TestNode, 1);
31 
32  node->test_name = test_name;
33  node->exp = exp ? exp : test_name;
34  node->should_succeed = TRUE;
35  node->expected_result = result;
36  node->file = file;
37  node->line = line;
38 
39  tests = g_list_append (tests, node);
40 }
41 
42 
43 #define add_fail_test(n,e,o) _add_fail_test((n), (e), (o), __FILE__, __LINE__)
44 
45 static void
46 _add_fail_test (const char *test_name, const char *exp, int expected_error_offset, char *file, int line)
47 {
48  TestNode *node = g_new0 (TestNode, 1);
49 
50  node->test_name = test_name;
51  node->exp = exp ? exp : test_name;
52  node->should_succeed = FALSE;
53  node->expected_error_offset = expected_error_offset;
54  node->file = file;
55  node->line = line;
56 
57  tests = g_list_append (tests, node);
58 }
59 
60 static void
61 run_parser_test (TestNode *node)
62 {
63  gboolean succeeded;
64  gnc_numeric result;
65  char *error_loc;
66  gchar *msg = "[func_op()] function eval error: [[func_op(]\n";
67  guint loglevel = G_LOG_LEVEL_CRITICAL, hdlr;
68  TestErrorStruct check = { loglevel, "gnc.gui", msg };
69 
70  result = gnc_numeric_error( -1 );
71  hdlr = g_log_set_handler ("gnc.gui", loglevel,
72  (GLogFunc)test_checked_handler, &check);
73  g_test_message ("Running test \"%s\" [%s] = ", node->test_name, node->exp);
74  succeeded = gnc_exp_parser_parse (node->exp, &result, &error_loc);
75  g_log_remove_handler ("gnc.gui", hdlr);
76  {
77  int pass;
78  pass = (succeeded == node->should_succeed);
79  if ( pass && node->should_succeed )
80  {
81  pass &= gnc_numeric_equal( result, node->expected_result );
82  }
83  g_test_message ( "%0.4f [%s]\n",
84  gnc_numeric_to_double( result ),
85  (pass ? "PASS" : "FAIL" ) );
86  }
87 
88  if (succeeded != node->should_succeed)
89  {
90  failure_args (node->test_name, node->file, node->line,
91  "parser %s on \"%s\"",
92  succeeded ? "succeeded" : "failed",
93  node->exp);
94  return;
95  }
96 
97  if (succeeded)
98  {
99  if (!gnc_numeric_equal (result, node->expected_result))
100  {
101  failure_args (node->test_name, node->file, node->line, "wrong result");
102  return;
103  }
104  }
105  else if (node->expected_error_offset != -1)
106  {
107  if (error_loc != node->exp + node->expected_error_offset)
108  {
109  failure_args (node->test_name, node->file, node->line, "wrong offset; expected %d, got %d",
110  node->expected_error_offset, (error_loc - node->exp));
111  return;
112  }
113  }
114 
115  success (node->test_name);
116 }
117 
118 static void
119 run_parser_tests (void)
120 {
121  GList *node;
122 
123  for (node = tests; node; node = node->next)
124  run_parser_test (node->data);
125 }
126 
127 static void
128 test_parser (void)
129 {
130  gnc_exp_parser_init ();
131  success ("initialize expression parser");
132 
133  add_fail_test ("null expression", NULL, -1);
134  add_fail_test ("empty expression", "", 0);
135  add_fail_test ("whitespace", " \t\n", 4);
136  add_fail_test ("bad expression", "\\", 0);
137  add_fail_test ("bad expression", "1 +", 3);
138  /* Bug#334811 - http://bugzilla.gnome.org/show_bug.cgi?id=334811 */
139  add_fail_test ("bad expression", "1 2", 3);
140  /* Bug#308554 - http://bugzilla.gnome.org/show_bug.cgi?id=308554 */
141  add_fail_test ("bad expression", "1 ç", 2);
142  add_fail_test ("bad expression", "ç 1", 0);
143  add_fail_test ("bad expression", "1 asdf", 6);
144  add_fail_test ("bad expression", "asdf 1", 6);
145  add_fail_test ("bad expression", "asdf jkl", 8);
146  add_fail_test ("bad expression", " (5 + 23)/ ", 14);
147  add_fail_test ("bad expression", " ((((5 + 23)/ ", 17);
148  add_fail_test ("divide by zero", " 4 / (1 - 1)", -1);
149 
150  add_pass_test ("zero", "0", gnc_numeric_zero ());
151  add_pass_test ("zero with whitespace", "\n\t 0 ", gnc_numeric_zero ());
152  add_pass_test ("1 + 2", NULL, gnc_numeric_create (3, 1));
153  add_pass_test ("17.3 - 12.3000", NULL, gnc_numeric_create (5, 1));
154  add_pass_test ("5 * 6", NULL, gnc_numeric_create (30, 1));
155  add_pass_test (" 34 / (22) ", NULL, gnc_numeric_create (34, 22));
156  add_pass_test (" (4 + 5 * 2) - 7 / 3", NULL, gnc_numeric_create (35, 3));
157  add_pass_test( "(a = 42) + (b = 12) - a", NULL, gnc_numeric_create( 12, 1 ) );
158  add_fail_test( "AUD $1.23", NULL, 4);
159  add_fail_test( "AUD $0.0", NULL, 4);
160  add_fail_test( "AUD 1.23", NULL, 8);
161  add_fail_test( "AUD 0.0", NULL, 7);
162  add_fail_test( "AUD 1.2 + CAN 2.3", NULL, 7);
163  add_fail_test( "AUD $1.2 + CAN $2.3", NULL, 4);
164 
165  add_pass_test( "1 + 2 * 3 + 4 + 5 * 6 * 7", NULL, gnc_numeric_create(221, 1) );
166  add_pass_test( "1 - 2 * 3 + 4 - 5 * 6 * 7", NULL, gnc_numeric_create(-211, 1) );
167  add_pass_test( "Conrad's bug",
168  "22.32 * 2 + 16.8 + 34.2 * 2 + 18.81 + 85.44"
169  "- 42.72 + 13.32 + 15.48 + 23.4 + 115.4",
170  gnc_numeric_create(35897, 100) );
171 
172  /* This must be defined for the function-parsing to work. */
173  scm_c_eval_string("(define (gnc:error->string tag args) (define (write-error port) (if (and (list? args) (not (null? args))) (let ((func (car args))) (if func (begin (display \"Function: \" port) (display func port) (display \", \" port) (display tag port) (display \"\n\n\" port))))) (false-if-exception (apply display-error (fluid-ref the-last-stack) port args)) (display-backtrace (fluid-ref the-last-stack) port) (force-output port)) (false-if-exception (call-with-output-string write-error)))");
174 
175  scm_c_eval_string( "(define (gnc:plus a b) (+ a b))" );
176  add_pass_test("plus(2 : 1)", NULL, gnc_numeric_create(3, 1));
177  add_fail_test("plus(1:2) plus(3:4)", NULL, 15);
178  add_pass_test( "plus( 1 : 2 ) + 3", NULL, gnc_numeric_create( 6, 1 ) );
179  add_pass_test( "plus( 1 : 2 ) * 3", NULL, gnc_numeric_create( 9, 1 ) );
180  add_pass_test( "plus( 1 + 2 : 3 ) * 5", NULL, gnc_numeric_create( 30, 1 ) );
181  add_pass_test( "plus( ( 1 + 2 ) * 3 : 4 ) + 5", NULL, gnc_numeric_create( 18, 1) );
182  add_pass_test( "5 + plus( ( 1 + 2 ) * 3 : 4 )", NULL, gnc_numeric_create( 18, 1) );
183  add_pass_test( "plus( plus( 1 : 2 ) : 3 )", NULL, gnc_numeric_create( 6, 1 ) );
184  add_pass_test( "plus( 4 : plus( plus( 1 : 2 ) : 3))", NULL, gnc_numeric_create( 10, 1 ) );
185 
186  scm_c_eval_string( "(define (gnc:sub a b) (- a b))" );
187  add_pass_test( "sub( 1 : 2 ) + 4", NULL, gnc_numeric_create( 3, 1 ) );
188 
189  add_pass_test( "sub( (1 + 2 * 3) : 4 ) + 5",
190  NULL, gnc_numeric_create( 8, 1 ) );
191  add_pass_test( "sub( 1 : 2 ) + sub( 3 : 4 ) + 5",
192  NULL, gnc_numeric_create( 3, 1 ) );
193  add_pass_test( "sub( a = 42 : sub( plus( 1 : 2 ) : 6 * 7 )) + a",
194  NULL, gnc_numeric_create( 123, 1 ) );
195 
196  scm_c_eval_string( "(define (gnc:test_str str b)"
197  " (+ b (cond ((equal? str \"one\") 1)"
198  " ((equal? str \"two\") 2)"
199  " ((equal? str \"three\") 3)"
200  " (0))))" );
201  add_pass_test( "test_str( \"one\" : 1 )", NULL, gnc_numeric_create( 2, 1 ) );
202  add_pass_test( "test_str( \"two\" : 2 )", NULL, gnc_numeric_create( 4, 1 ) );
203  add_fail_test( "test_str( 3 : \"three\" )", NULL, 23 );
204  add_pass_test( "test_str( \"asdf\" : 1 )", NULL, gnc_numeric_create( 1, 1 ) );
205  add_fail_test("\"asdf\" + 0", NULL, 8);
206 
207  scm_c_eval_string( "(define (gnc:blindreturn val) val)" );
208  add_pass_test( "blindreturn( 123.1 )", NULL, gnc_numeric_create( 1231, 10 ) );
209  add_pass_test( "blindreturn( 123.01 )", NULL, gnc_numeric_create( 12301, 100 ) );
210  add_pass_test( "blindreturn( 123.001 )", NULL, gnc_numeric_create( 123001, 1000 ) );
211 
212  run_parser_tests ();
213 
214  gnc_exp_parser_shutdown ();
215  success ("shutdown expression parser");
216 }
217 
218 static void
219 test_variable_expressions()
220 {
221  gnc_numeric num;
222  gchar *errLoc = NULL;
223  GHashTable *vars = g_hash_table_new(g_str_hash, g_str_equal);
224  do_test(gnc_exp_parser_parse_separate_vars("123 + a", &num, &errLoc, vars), "parsing");
225  do_test(g_hash_table_size(vars) == 1, "'a' is the variable; good job, gnc-exp-parser!");
226  success("variable found");
227 }
228 
229 static void
230 real_main (void *closure, int argc, char **argv)
231 {
232  /* set_should_print_success (TRUE); */
233  test_parser();
234  test_variable_expressions();
235  print_test_results();
236  exit(get_rv());
237 }
238 
239 int main ( int argc, char **argv )
240 {
241  /* do things this way so we can test scheme function calls from expressions */
242  scm_boot_guile( argc, argv, real_main, NULL );
243  return 0;
244 }
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
An exact-rational-number library for gnucash. (to be renamed qofnumeric.h in libqof2) ...
gdouble gnc_numeric_to_double(gnc_numeric n)
gnc_numeric gnc_numeric_error(GNCNumericErrorCode error_code)