FORS Pipeline Reference Manual  5.0.9
test.c
1 /* *
2  * This file is part of the ESO FORS package *
3  * Copyright (C) 2004,2005 European Southern Observatory *
4  * *
5  * This library is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the Free Software *
17  * Foundation, 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA *
18  * */
19 
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23 
24 /*-----------------------------------------------------------------------------
25  Includes
26  -----------------------------------------------------------------------------*/
27 
28 #include <test.h>
29 
30 #include <fors_utils.h>
31 
32 #include <cpl.h>
33 #include <math.h> /* fabs() */
34 #include <string.h> /* strlen() */
35 
36 /*----------------------------------------------------------------------------*/
40 /*----------------------------------------------------------------------------*/
41 
42 /*-----------------------------------------------------------------------------
43  Implementation
44  -----------------------------------------------------------------------------*/
45 
49 static unsigned long test_nfail = 0;
50 static cpl_errorstate error_init;
51 
52 /*----------------------------------------------------------------------------*/
61 /*----------------------------------------------------------------------------*/
62 static void
63 _test(int expression, const char *message,
64  const char *function, const char *file, unsigned line)
65 {
66  const char *error_state = (cpl_error_get_code() != CPL_ERROR_NONE) ?
67  cpl_sprintf(" (CPL-error state: '%s' at %s)",
68  cpl_error_get_message(), cpl_error_get_where()) :
69  NULL;
70 
71  if (expression) {
72  cpl_msg_debug(function,
73  "OK at %s:%u%s: %s",
74  file, line, error_state != NULL ? error_state : "",
75  message);
76  } else {
77  if (test_nfail + 1 > test_nfail) {
78  test_nfail++;
79  }
80  else {
81  cpl_msg_error(function, "Number of errors (%lu) overflow!",
82  test_nfail);
83  }
84 
85  cpl_msg_error(function,
86  "Failure at %s:%u%s: %s",
87  file, line, error_state != NULL ? error_state : "",
88  message);
89  }
90 
91  if (error_state != NULL)
92  {
93  cpl_free((char *)error_state);
94  }
95 
96  return;
97 }
98 
99 
100 /*----------------------------------------------------------------------------*/
110 /*----------------------------------------------------------------------------*/
111 void
112 test_macro(int expression, const char *expr_string,
113  const char *function, const char *file, unsigned line)
114 {
115  const char *message = cpl_sprintf("(%s) = %d", expr_string, expression);
116 
117  _test(expression, message,
118  function, file, line);
119 
120  cpl_free((char *)message);
121 
122  return;
123 }
124 
125 /*----------------------------------------------------------------------------*/
137 /*----------------------------------------------------------------------------*/
138 void
139 test_eq_macro(int first, const char *first_string,
140  int second, const char *second_string,
141  const char *function, const char *file, unsigned line)
142 {
143  const char *message =
144  cpl_sprintf("(%s) = %d; (%s) = %d",
145  first_string, first,
146  second_string, second);
147 
148  _test(first == second, message,
149  function, file, line);
150 
151  cpl_free((char *)message);
152 
153  return;
154 }
155 
156 /*----------------------------------------------------------------------------*/
168 /*----------------------------------------------------------------------------*/
169 void
170 test_eq_string_macro(const char *first, const char *first_string,
171  const char *second, const char *second_string,
172  const char *function,
173  const char *file, unsigned line)
174 {
175  const char *message;
176 
177  message = cpl_sprintf("%s = '%s'; %s = '%s'",
178  first_string, first != NULL ? first : "NULL",
179  second_string, second != NULL ? second : "NULL");
180 
181  _test(first != NULL && second != NULL && strcmp(first, second) == 0,
182  message,
183  function, file, line);
184 
185  cpl_free((char *)message);
186 
187  return;
188 }
189 
190 /*----------------------------------------------------------------------------*/
206 /*----------------------------------------------------------------------------*/
207 void
208 test_abs_macro(double first, const char *first_string,
209  double second, const char *second_string,
210  double tolerance, const char *tolerance_string,
211  const char *function, const char *file, unsigned line)
212 {
213  const char *message =
214  cpl_sprintf("|%s - (%s)| = |%g - (%g)| <= %g = %s",
215  first_string, second_string, first, second,
216  tolerance, tolerance_string);
217 
218  _test(fabs(first - second) <= tolerance, message,
219  function, file, line);
220  /* Note: fails if tolerance is negative */
221 
222  cpl_free((char *)message);
223 
224  return;
225 }
226 
227 /*----------------------------------------------------------------------------*/
243 /*----------------------------------------------------------------------------*/
244 void
245 test_rel_macro(double first, const char *first_string,
246  double second, const char *second_string,
247  double tolerance, const char *tolerance_string,
248  const char *function, const char *file, unsigned line)
249 {
250  const char *message;
251 
252  if (first == 0 || second == 0) {
253  /* Division by zero -> fail */
254  message = cpl_sprintf("%s = %g; %s = %g (division by zero)",
255  first_string, first,
256  second_string, second);
257  _test(0, message,
258  function, file, line);
259  }
260  else {
261  message =
262  cpl_sprintf("|%s - (%s)|/|%s| = |%g - (%g)|/|%g| <= %g = %s and "
263  "|%s - (%s)|/|%s| = |%g - (%g)|/|%g| <= %g = %s",
264  first_string, second_string, first_string,
265  first, second, first, tolerance, tolerance_string,
266  first_string, second_string, second_string,
267  first, second, second, tolerance, tolerance_string);
268 
269  _test(fabs((first - second)/first ) <= tolerance &&
270  fabs((first - second)/second) <= tolerance, message,
271  function, file, line);
272  /* Note: fails if tolerance is negative */
273  }
274 
275  cpl_free((char *)message);
276 
277  return;
278 }
279 
280 
281 /*----------------------------------------------------------------------------*/
288 /*----------------------------------------------------------------------------*/
289 void
290 test_init_macro(const char *file)
291 {
292  /* Build message domain + log filename
293  from source file name */
294 
295  unsigned len = strlen(file);
296  int able_to_parse_filename;
297 
298  cpl_init(CPL_INIT_DEFAULT);
299  error_init = cpl_errorstate_get();
300  srand(0);
301 
302  if (len < strlen("s.c") || strcmp(file + len - strlen(".c"), ".c") != 0)
303  {
304  able_to_parse_filename = 0;
305  /* Here, we should issue a warning but we need to
306  initialize the CPL message system first */
307  }
308  else
309  {
310  /* Form domain from filename by removing ".c" suffix
311  and stripping any path prefix */
312 
313  const char dir_delimiter = '/'; /* UNIX only! */
314  unsigned path_length = 0; /* Length of path prefix
315  including final '/' */
316  int i;
317 
318  for (i = len - strlen(".c") - 1; i >= 0; i--)
319  {
320  if (file[i] == dir_delimiter && path_length == 0)
321  {
322  path_length = i + 1;
323  }
324  }
325 
326  able_to_parse_filename = (len - path_length - strlen(".c") >= 1);
327  if (able_to_parse_filename)
328  {
329  const char *logfile;
330  char *domain;
331 
332  domain = cpl_strdup(file + path_length);
333  domain[len - path_length - strlen(".c")] = '\0';
334 
335  cpl_msg_set_domain(domain);
336 
337  logfile = cpl_sprintf("%s.log", domain);
338  cpl_msg_set_log_name(logfile);
339 
340  cpl_free((char *)logfile);
341  cpl_free(domain);
342  }
343  /* else: Use default domain + logfilename */
344  }
345 
346  /* Adopt the policy that the unit test is quiet
347  unless something is wrong.
348  Dump debugging info to the log file */
349  cpl_msg_set_level(CPL_MSG_WARNING);
350  cpl_msg_set_log_level(CPL_MSG_DEBUG);
351 
352  if (!able_to_parse_filename)
353  {
354  cpl_msg_warning(cpl_func,
355  "Source file name '%s' does not match [base].c. ",
356  file);
357  }
358 
359  if (cpl_error_get_code() != CPL_ERROR_NONE)
360  {
361  cpl_msg_error(cpl_func,
362  "Failure during initialization: %s at %s",
363  cpl_error_get_message(), cpl_error_get_where());
364  }
365 
366  return;
367 }
368 
369 /*----------------------------------------------------------------------------*/
379 /*----------------------------------------------------------------------------*/
380 unsigned
381 test_end_macro(const char *function, const char *file, unsigned line)
382 {
383  const int memory_is_empty = cpl_memory_is_empty();
384 
385  test_eq_macro(cpl_error_get_code(), "cpl_error_get_code()",
386  CPL_ERROR_NONE, "CPL_ERROR_NONE",
387  function, file, line);
388 
389  if (cpl_error_get_code() != CPL_ERROR_NONE) {
390  cpl_errorstate_dump(error_init, CPL_FALSE, NULL);
391  }
392 
393  test_macro(memory_is_empty,
394  "memory_is_empty",
395  function, file, line);
396 
397  if (!memory_is_empty) {
398  cpl_msg_error(function, "Memory leak detected:");
399  cpl_memory_dump();
400  }
401 
402  cpl_end();
403 
404  return test_nfail;
405 }
406 
407 
408 
409 
410 
411 #undef cleanup
412 #define cleanup \
413 do { \
414  cpl_propertylist_delete(product_header); \
415 } while(0)
416 /*----------------------------------------------------------------------------*/
426 /*----------------------------------------------------------------------------*/
427 void
428 test_recipe_output(const cpl_frameset *frames,
429  const char *const product_tags[], int n_prod,
430  const char *main_product,
431  const char *const qc[], int n_qc)
432 {
433  cpl_propertylist *product_header = NULL;
434 
435  int i;
436  for (i = 0; i < n_prod; i++) {
437  const cpl_frame *product = cpl_frameset_find_const(frames, product_tags[i]);
438  test( product != NULL );
439  test_eq( cpl_frame_get_group(product), CPL_FRAME_GROUP_PRODUCT );
440 
441  cpl_propertylist_delete(product_header);
442  product_header = cpl_propertylist_load(cpl_frame_get_filename(product), 0);
443 
444  assure( !cpl_error_get_code(), return,
445  "Failed to load product header" );
446 
447 
448  if (strcmp(product_tags[i], main_product) != 0) {
449  test( !cpl_propertylist_has(product_header,
450  "ESO QC DID") );
451  }
452 
453  int j;
454  for (j = 0; j < n_qc; j++) {
455 
456  const char *full_qc_name = cpl_sprintf("ESO %s", qc[j]);
457 
458  cpl_msg_debug(cpl_func,
459  "Looking for '%s' in '%s'",
460  qc[j], product_tags[i]);
461 
462  if (strcmp(product_tags[i], main_product) == 0) {
463  test( cpl_propertylist_has(product_header,
464  full_qc_name) );
465  }
466  else {
467  test( !cpl_propertylist_has(product_header,
468  full_qc_name) );
469  }
470 
471  cpl_free((void *)full_qc_name);
472  }
473  }
474 
475  cleanup;
476  return;
477 }
478 
void test_macro(int expression, const char *expr_string, const char *function, const char *file, unsigned line)
Test a given expression.
Definition: test.c:112
void test_abs_macro(double first, const char *first_string, double second, const char *second_string, double tolerance, const char *tolerance_string, const char *function, const char *file, unsigned line)
Test if two numerical expressions are within a given (absolute) tolerance.
Definition: test.c:208
void test_eq_macro(int first, const char *first_string, int second, const char *second_string, const char *function, const char *file, unsigned line)
Test if two integer expressions are equal.
Definition: test.c:139
static unsigned long test_nfail
Definition: test.c:49
void test_rel_macro(double first, const char *first_string, double second, const char *second_string, double tolerance, const char *tolerance_string, const char *function, const char *file, unsigned line)
Test if two numerical expressions are within a given relative tolerance.
Definition: test.c:245
unsigned test_end_macro(const char *function, const char *file, unsigned line)
Perform the final checks and return the number of errors.
Definition: test.c:381
#define assure(EXPR)
Definition: list.c:101
void test_eq_string_macro(const char *first, const char *first_string, const char *second, const char *second_string, const char *function, const char *file, unsigned line)
Test if two strings are equal.
Definition: test.c:170
void test_init_macro(const char *file)
Initialize CPL + messaging.
Definition: test.c:290
void test_recipe_output(const cpl_frameset *frames, const char *const product_tags[], int n_prod, const char *main_product, const char *const qc[], int n_qc)
Test existence of recipe products.
Definition: test.c:428
static void _test(int expression, const char *message, const char *function, const char *file, unsigned line)
Evaluate an expression and update an internal counter if it fails.
Definition: test.c:63