po: Update ca,es,de,nl,uk translations from translationproject.org.
[pspp.git] / tests / data / datasheet-test.c
blobf0384853e60ccde412a3633f08ca40dd0a698b9a
1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2007, 2009, 2010, 2014 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
17 #include <config.h>
19 #include <data/datasheet.h>
21 #include <ctype.h>
22 #include <float.h>
23 #include <stdlib.h>
24 #include <stdint.h>
25 #include <string.h>
27 #include <data/casereader-provider.h>
28 #include <data/casereader.h>
29 #include <data/casewriter.h>
30 #include <data/lazy-casereader.h>
31 #include <libpspp/argv-parser.h>
32 #include <libpspp/array.h>
33 #include <libpspp/assertion.h>
34 #include <libpspp/hash-functions.h>
35 #include <libpspp/model-checker.h>
36 #include <libpspp/range-map.h>
37 #include <libpspp/range-set.h>
38 #include <libpspp/str.h>
39 #include <libpspp/taint.h>
40 #include <libpspp/tower.h>
42 #include "error.h"
43 #include "minmax.h"
44 #include "progname.h"
46 /* lazy_casereader callback function to instantiate a casereader
47 from the datasheet. */
48 static struct casereader *
49 lazy_callback (void *ds_)
51 struct datasheet *ds = ds_;
52 return datasheet_make_reader (ds);
56 /* Maximum size of datasheet supported for model checking
57 purposes. */
58 #define MAX_ROWS 5
59 #define MAX_COLS 5
60 #define MAX_WIDTHS 5
62 /* Test params. */
63 struct datasheet_test_params
65 /* Parameters. */
66 int max_rows; /* Maximum number of rows. */
67 int max_cols; /* Maximum number of columns. */
68 int backing_rows; /* Number of rows of backing store. */
69 int backing_widths[MAX_COLS]; /* Widths of columns of backing store. */
70 int n_backing_cols; /* Number of columns of backing store. */
71 int widths[MAX_WIDTHS]; /* Allowed column widths. */
72 int n_widths;
74 /* State. */
75 unsigned int next_value;
78 static bool
79 check_caseproto (struct mc *mc, const struct caseproto *benchmark,
80 const struct caseproto *test, const char *test_name)
82 size_t n_columns = caseproto_get_n_widths (benchmark);
83 size_t col;
84 bool ok;
86 if (n_columns != caseproto_get_n_widths (test))
88 mc_error (mc, "%s column count (%zu) does not match expected (%zu)",
89 test_name, caseproto_get_n_widths (test), n_columns);
90 return false;
93 ok = true;
94 for (col = 0; col < n_columns; col++)
96 int benchmark_width = caseproto_get_width (benchmark, col);
97 int test_width = caseproto_get_width (test, col);
98 if (benchmark_width != test_width)
100 mc_error (mc, "%s column %zu width (%d) differs from expected (%d)",
101 test_name, col, test_width, benchmark_width);
102 ok = false;
105 return ok;
108 /* Checks that READER contains the N_ROWS rows and N_COLUMNS
109 columns of data in ARRAY, reporting any errors via MC. */
110 static void
111 check_datasheet_casereader (struct mc *mc, struct casereader *reader,
112 union value array[MAX_ROWS][MAX_COLS],
113 size_t n_rows, const struct caseproto *proto)
115 size_t n_columns = caseproto_get_n_widths (proto);
117 if (!check_caseproto (mc, proto, casereader_get_proto (reader),
118 "casereader"))
119 return;
120 else if (casereader_get_n_cases (reader) != n_rows)
122 if (casereader_get_n_cases (reader) == CASENUMBER_MAX
123 && casereader_count_cases (reader) == n_rows)
124 mc_error (mc, "datasheet casereader has unknown case count");
125 else
126 mc_error (mc, "casereader row count (%lu) does not match "
127 "expected (%zu)",
128 (unsigned long int) casereader_get_n_cases (reader),
129 n_rows);
131 else
133 struct ccase *c;
134 size_t row;
136 for (row = 0; row < n_rows; row++)
138 size_t col;
140 c = casereader_read (reader);
141 if (c == NULL)
143 mc_error (mc, "casereader_read failed reading row %zu of %zu "
144 "(%zu columns)", row, n_rows, n_columns);
145 return;
148 for (col = 0; col < n_columns; col++)
150 int width = caseproto_get_width (proto, col);
151 if (!value_equal (case_data_idx (c, col), &array[row][col],
152 width))
154 if (width == 0)
155 mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: "
156 "%.*g != %.*g",
157 row, col, n_rows, n_columns,
158 DBL_DIG + 1, case_num_idx (c, col),
159 DBL_DIG + 1, array[row][col].f);
160 else
161 mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: "
162 "'%.*s' != '%.*s'",
163 row, col, n_rows, n_columns,
164 width, case_str_idx (c, col),
165 width, array[row][col].s);
169 case_unref (c);
172 c = casereader_read (reader);
173 if (c != NULL)
174 mc_error (mc, "casereader has extra cases (expected %zu)", n_rows);
178 /* Checks that datasheet DS contains has N_ROWS rows, N_COLUMNS
179 columns, and the same contents as ARRAY, reporting any
180 mismatches via mc_error. Then, adds DS to MC as a new state. */
181 static void
182 check_datasheet (struct mc *mc, struct datasheet *ds,
183 union value array[MAX_ROWS][MAX_COLS],
184 size_t n_rows, const struct caseproto *proto)
186 size_t n_columns = caseproto_get_n_widths (proto);
187 struct datasheet *ds2;
188 struct casereader *reader;
189 unsigned long int serial = 0;
191 assert (n_rows < MAX_ROWS);
192 assert (n_columns < MAX_COLS);
194 /* Check contents of datasheet via datasheet functions. */
195 if (!check_caseproto (mc, proto, datasheet_get_proto (ds), "datasheet"))
197 /* check_caseproto emitted errors already. */
199 else if (n_rows != datasheet_get_n_rows (ds))
200 mc_error (mc, "row count (%lu) does not match expected (%zu)",
201 (unsigned long int) datasheet_get_n_rows (ds), n_rows);
202 else
204 size_t row, col;
205 bool difference = false;
207 for (row = 0; row < n_rows; row++)
208 for (col = 0; col < n_columns; col++)
210 int width = caseproto_get_width (proto, col);
211 union value *av = &array[row][col];
212 union value v;
214 value_init (&v, width);
215 if (!datasheet_get_value (ds, row, col, &v))
216 NOT_REACHED ();
217 if (!value_equal (&v, av, width))
219 if (width == 0)
220 mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: "
221 "%.*g != %.*g", row, col, n_rows, n_columns,
222 DBL_DIG + 1, v.f, DBL_DIG + 1, av->f);
223 else
224 mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: "
225 "'%.*s' != '%.*s'",
226 row, col, n_rows, n_columns,
227 width, v.s,
228 width, v.s);
229 difference = true;
231 value_destroy (&v, width);
234 if (difference)
236 struct string s;
238 mc_error (mc, "expected:");
239 ds_init_empty (&s);
240 for (row = 0; row < n_rows; row++)
242 ds_clear (&s);
243 ds_put_format (&s, "row %zu:", row);
244 for (col = 0; col < n_columns; col++)
246 const union value *v = &array[row][col];
247 int width = caseproto_get_width (proto, col);
248 if (width == 0)
249 ds_put_format (&s, " %g", v->f);
250 else
251 ds_put_format (&s, " '%.*s'", width, v->s);
253 mc_error (mc, "%s", ds_cstr (&s));
256 mc_error (mc, "actual:");
257 ds_init_empty (&s);
258 for (row = 0; row < n_rows; row++)
260 ds_clear (&s);
261 ds_put_format (&s, "row %zu:", row);
262 for (col = 0; col < n_columns; col++)
264 int width = caseproto_get_width (proto, col);
265 union value v;
266 value_init (&v, width);
267 if (!datasheet_get_value (ds, row, col, &v))
268 NOT_REACHED ();
269 if (width == 0)
270 ds_put_format (&s, " %g", v.f);
271 else
272 ds_put_format (&s, " '%.*s'", width, v.s);
274 mc_error (mc, "%s", ds_cstr (&s));
277 ds_destroy (&s);
281 /* Check that datasheet contents are correct when read through
282 casereader. */
283 ds2 = clone_datasheet (ds);
284 reader = datasheet_make_reader (ds2);
285 check_datasheet_casereader (mc, reader, array, n_rows, proto);
286 casereader_destroy (reader);
288 /* Check that datasheet contents are correct when read through
289 casereader with lazy_casereader wrapped around it. This is
290 valuable because otherwise there is no non-GUI code that
291 uses the lazy_casereader. */
292 ds2 = clone_datasheet (ds);
293 reader = lazy_casereader_create (datasheet_get_proto (ds2), n_rows,
294 lazy_callback, ds2, &serial);
295 check_datasheet_casereader (mc, reader, array, n_rows, proto);
296 if (lazy_casereader_destroy (reader, serial))
298 /* Lazy casereader was never instantiated. This will
299 only happen if there are no rows (because in that case
300 casereader_read never gets called). */
301 datasheet_destroy (ds2);
302 if (n_rows != 0)
303 mc_error (mc, "lazy casereader not instantiated, but should "
304 "have been (size %zu,%zu)", n_rows, n_columns);
306 else
308 /* Lazy casereader was instantiated. This is the common
309 case, in which some casereader operation
310 (casereader_read in this case) was performed on the
311 lazy casereader. */
312 casereader_destroy (reader);
313 if (n_rows == 0)
314 mc_error (mc, "lazy casereader instantiated, but should not "
315 "have been (size %zu,%zu)", n_rows, n_columns);
318 if (mc_discard_dup_state (mc, hash_datasheet (ds)))
319 datasheet_destroy (ds);
320 else
321 mc_add_state (mc, ds);
324 /* Extracts the contents of DS into DATA. */
325 static void
326 extract_data (const struct datasheet *ds, union value data[MAX_ROWS][MAX_COLS])
328 const struct caseproto *proto = datasheet_get_proto (ds);
329 size_t n_columns = datasheet_get_n_columns (ds);
330 size_t n_rows = datasheet_get_n_rows (ds);
331 size_t row, col;
333 assert (n_rows < MAX_ROWS);
334 assert (n_columns < MAX_COLS);
335 for (row = 0; row < n_rows; row++)
336 for (col = 0; col < n_columns; col++)
338 int width = caseproto_get_width (proto, col);
339 union value *v = &data[row][col];
340 value_init (v, width);
341 if (!datasheet_get_value (ds, row, col, v))
342 NOT_REACHED ();
346 /* Copies the contents of ODATA into DATA. Each of the N_ROWS
347 rows of ODATA and DATA must have prototype PROTO. */
348 static void
349 clone_data (size_t n_rows, const struct caseproto *proto,
350 union value odata[MAX_ROWS][MAX_COLS],
351 union value data[MAX_ROWS][MAX_COLS])
353 size_t n_columns = caseproto_get_n_widths (proto);
354 size_t row, col;
356 assert (n_rows < MAX_ROWS);
357 assert (n_columns < MAX_COLS);
358 for (row = 0; row < n_rows; row++)
359 for (col = 0; col < n_columns; col++)
361 int width = caseproto_get_width (proto, col);
362 const union value *ov = &odata[row][col];
363 union value *v = &data[row][col];
364 value_init (v, width);
365 value_copy (v, ov, width);
369 static void
370 release_data (size_t n_rows, const struct caseproto *proto,
371 union value data[MAX_ROWS][MAX_COLS])
373 size_t n_columns = caseproto_get_n_widths (proto);
374 size_t row, col;
376 assert (n_rows < MAX_ROWS);
377 assert (n_columns < MAX_COLS);
378 for (col = 0; col < n_columns; col++)
380 int width = caseproto_get_width (proto, col);
381 if (value_needs_init (width))
382 for (row = 0; row < n_rows; row++)
383 value_destroy (&data[row][col], width);
387 /* Clones the structure and contents of ODS into *DS,
388 and the contents of ODATA into DATA. */
389 static void
390 clone_model (const struct datasheet *ods,
391 union value odata[MAX_ROWS][MAX_COLS],
392 struct datasheet **ds,
393 union value data[MAX_ROWS][MAX_COLS])
395 *ds = clone_datasheet (ods);
396 clone_data (datasheet_get_n_rows (ods), datasheet_get_proto (ods),
397 odata, data);
400 static void
401 value_from_param (union value *value, int width, unsigned int idx)
403 if (width == 0)
404 value->f = idx & 0xffff;
405 else
407 unsigned int hash = hash_int (idx, 0);
408 int offset;
410 assert (width < 32);
411 for (offset = 0; offset < width; offset++)
412 value->s[offset] = "ABCDEFGHIJ"[(hash >> offset) % 10];
416 /* "init" function for struct mc_class. */
417 static void
418 datasheet_mc_init (struct mc *mc)
420 struct datasheet_test_params *params = mc_get_aux (mc);
421 struct datasheet *ds;
423 if (params->backing_rows == 0 && params->n_backing_cols == 0)
425 /* Create unbacked datasheet. */
426 struct caseproto *proto;
427 ds = datasheet_create (NULL);
428 mc_name_operation (mc, "empty datasheet");
429 proto = caseproto_create ();
430 check_datasheet (mc, ds, NULL, 0, proto);
431 caseproto_unref (proto);
433 else
435 /* Create datasheet with backing. */
436 struct casewriter *writer;
437 struct casereader *reader;
438 union value data[MAX_ROWS][MAX_COLS];
439 struct caseproto *proto;
440 int row, col;
442 assert (params->backing_rows > 0 && params->backing_rows <= MAX_ROWS);
443 assert (params->n_backing_cols > 0
444 && params->n_backing_cols <= MAX_COLS);
446 proto = caseproto_create ();
447 for (col = 0; col < params->n_backing_cols; col++)
448 proto = caseproto_add_width (proto, params->backing_widths[col]);
450 writer = mem_writer_create (proto);
451 for (row = 0; row < params->backing_rows; row++)
453 struct ccase *c;
455 c = case_create (proto);
456 for (col = 0; col < params->n_backing_cols; col++)
458 int width = params->backing_widths[col];
459 union value *value = &data[row][col];
460 value_init (value, width);
461 value_from_param (value, width, params->next_value++);
462 value_copy (case_data_rw_idx (c, col), value, width);
464 casewriter_write (writer, c);
467 reader = casewriter_make_reader (writer);
468 assert (reader != NULL);
470 ds = datasheet_create (reader);
471 mc_name_operation (mc, "datasheet with (%d,%d) backing",
472 params->backing_rows, params->n_backing_cols);
473 check_datasheet (mc, ds, data,
474 params->backing_rows, proto);
475 release_data (params->backing_rows, proto, data);
476 caseproto_unref (proto);
480 struct resize_cb_aux
482 int old_width;
483 int new_width;
486 static void
487 resize_cb (const union value *old_value, union value *new_value, const void *aux_)
489 const struct resize_cb_aux *aux = aux_;
491 value_from_param (new_value, aux->new_width,
492 value_hash (old_value, aux->old_width, 0));
495 /* "mutate" function for struct mc_class. */
496 static void
497 datasheet_mc_mutate (struct mc *mc, const void *ods_)
499 struct datasheet_test_params *params = mc_get_aux (mc);
501 const struct datasheet *ods = ods_;
502 union value odata[MAX_ROWS][MAX_COLS];
503 union value data[MAX_ROWS][MAX_COLS];
504 const struct caseproto *oproto = datasheet_get_proto (ods);
505 size_t n_columns = datasheet_get_n_columns (ods);
506 size_t n_rows = datasheet_get_n_rows (ods);
507 size_t pos, new_pos, n, width_idx;
509 extract_data (ods, odata);
511 /* Insert a column in each possible position. */
512 if (n_columns < params->max_cols)
513 for (pos = 0; pos <= n_columns; pos++)
514 for (width_idx = 0; width_idx < params->n_widths; width_idx++)
515 if (mc_include_state (mc))
517 int width = params->widths[width_idx];
518 struct caseproto *proto;
519 struct datasheet *ds;
520 union value new;
521 size_t i;
523 mc_name_operation (mc, "insert column at %zu "
524 "(from %zu to %zu columns)",
525 pos, n_columns, n_columns + 1);
526 clone_model (ods, odata, &ds, data);
528 value_init (&new, width);
529 value_from_param (&new, width, params->next_value++);
530 if (!datasheet_insert_column (ds, &new, width, pos))
531 mc_error (mc, "datasheet_insert_column failed");
532 proto = caseproto_insert_width (caseproto_ref (oproto),
533 pos, width);
535 for (i = 0; i < n_rows; i++)
537 insert_element (&data[i][0], n_columns, sizeof data[i][0],
538 pos);
539 value_init (&data[i][pos], width);
540 value_copy (&data[i][pos], &new, width);
542 value_destroy (&new, width);
544 check_datasheet (mc, ds, data, n_rows, proto);
545 release_data (n_rows, proto, data);
546 caseproto_unref (proto);
549 /* Resize each column to each possible new size. */
550 for (pos = 0; pos < n_columns; pos++)
551 for (width_idx = 0; width_idx < params->n_widths; width_idx++)
553 int owidth = caseproto_get_width (oproto, pos);
554 int width = params->widths[width_idx];
555 if (mc_include_state (mc))
557 struct resize_cb_aux aux;
558 struct caseproto *proto;
559 struct datasheet *ds;
560 size_t i;
562 mc_name_operation (mc, "resize column %zu (of %zu) "
563 "from width %d to %d",
564 pos, n_columns, owidth, width);
565 clone_model (ods, odata, &ds, data);
567 aux.old_width = owidth;
568 aux.new_width = width;
569 if (!datasheet_resize_column (ds, pos, width, resize_cb, &aux))
570 NOT_REACHED ();
571 proto = caseproto_set_width (caseproto_ref (oproto), pos, width);
573 for (i = 0; i < n_rows; i++)
575 union value *old_value = &data[i][pos];
576 union value new_value;
577 value_init (&new_value, width);
578 resize_cb (old_value, &new_value, &aux);
579 value_swap (old_value, &new_value);
580 value_destroy (&new_value, owidth);
583 check_datasheet (mc, ds, data, n_rows, proto);
584 release_data (n_rows, proto, data);
585 caseproto_unref (proto);
589 /* Delete all possible numbers of columns from all possible
590 positions. */
591 for (pos = 0; pos < n_columns; pos++)
592 for (n = 1; n < n_columns - pos; n++)
593 if (mc_include_state (mc))
595 struct caseproto *proto;
596 struct datasheet *ds;
597 size_t i, j;
599 mc_name_operation (mc, "delete %zu columns at %zu "
600 "(from %zu to %zu columns)",
601 n, pos, n_columns, n_columns - n);
602 clone_model (ods, odata, &ds, data);
604 datasheet_delete_columns (ds, pos, n);
605 proto = caseproto_remove_widths (caseproto_ref (oproto), pos, n);
607 for (i = 0; i < n_rows; i++)
609 for (j = pos; j < pos + n; j++)
610 value_destroy (&data[i][j], caseproto_get_width (oproto, j));
611 remove_range (&data[i], n_columns, sizeof *data[i], pos, n);
614 check_datasheet (mc, ds, data, n_rows, proto);
615 release_data (n_rows, proto, data);
616 caseproto_unref (proto);
619 /* Move all possible numbers of columns from all possible
620 existing positions to all possible new positions. */
621 for (pos = 0; pos < n_columns; pos++)
622 for (n = 1; n < n_columns - pos; n++)
623 for (new_pos = 0; new_pos < n_columns - n; new_pos++)
624 if (mc_include_state (mc))
626 struct caseproto *proto;
627 struct datasheet *ds;
628 size_t i;
630 clone_model (ods, odata, &ds, data);
631 mc_name_operation (mc, "move %zu columns (of %zu) from %zu to %zu",
632 n, n_columns, pos, new_pos);
634 datasheet_move_columns (ds, pos, new_pos, n);
636 for (i = 0; i < n_rows; i++)
637 move_range (&data[i], n_columns, sizeof data[i][0],
638 pos, new_pos, n);
639 proto = caseproto_move_widths (caseproto_ref (oproto),
640 pos, new_pos, n);
642 check_datasheet (mc, ds, data, n_rows, proto);
643 release_data (n_rows, proto, data);
644 caseproto_unref (proto);
647 /* Insert all possible numbers of rows in all possible
648 positions. */
649 for (pos = 0; pos <= n_rows; pos++)
650 for (n = 1; n <= params->max_rows - n_rows; n++)
651 if (mc_include_state (mc))
653 struct datasheet *ds;
654 struct ccase *c[MAX_ROWS];
655 size_t i, j;
657 clone_model (ods, odata, &ds, data);
658 mc_name_operation (mc, "insert %zu rows at %zu "
659 "(from %zu to %zu rows)",
660 n, pos, n_rows, n_rows + n);
662 for (i = 0; i < n; i++)
664 c[i] = case_create (oproto);
665 for (j = 0; j < n_columns; j++)
666 value_from_param (case_data_rw_idx (c[i], j),
667 caseproto_get_width (oproto, j),
668 params->next_value++);
671 insert_range (data, n_rows, sizeof data[pos], pos, n);
672 for (i = 0; i < n; i++)
673 for (j = 0; j < n_columns; j++)
675 int width = caseproto_get_width (oproto, j);
676 value_init (&data[i + pos][j], width);
677 value_copy (&data[i + pos][j], case_data_idx (c[i], j), width);
680 if (!datasheet_insert_rows (ds, pos, c, n))
681 mc_error (mc, "datasheet_insert_rows failed");
683 check_datasheet (mc, ds, data, n_rows + n, oproto);
684 release_data (n_rows + n, oproto, data);
687 /* Delete all possible numbers of rows from all possible
688 positions. */
689 for (pos = 0; pos < n_rows; pos++)
690 for (n = 1; n < n_rows - pos; n++)
691 if (mc_include_state (mc))
693 struct datasheet *ds;
695 clone_model (ods, odata, &ds, data);
696 mc_name_operation (mc, "delete %zu rows at %zu "
697 "(from %zu to %zu rows)",
698 n, pos, n_rows, n_rows - n);
700 datasheet_delete_rows (ds, pos, n);
702 release_data (n, oproto, &data[pos]);
703 remove_range (&data[0], n_rows, sizeof data[0], pos, n);
705 check_datasheet (mc, ds, data, n_rows - n, oproto);
706 release_data (n_rows - n, oproto, data);
709 /* Move all possible numbers of rows from all possible existing
710 positions to all possible new positions. */
711 for (pos = 0; pos < n_rows; pos++)
712 for (n = 1; n < n_rows - pos; n++)
713 for (new_pos = 0; new_pos < n_rows - n; new_pos++)
714 if (mc_include_state (mc))
716 struct datasheet *ds;
718 clone_model (ods, odata, &ds, data);
719 mc_name_operation (mc, "move %zu rows (of %zu) from %zu to %zu",
720 n, n_rows, pos, new_pos);
722 datasheet_move_rows (ds, pos, new_pos, n);
724 move_range (&data[0], n_rows, sizeof data[0],
725 pos, new_pos, n);
727 check_datasheet (mc, ds, data, n_rows, oproto);
728 release_data (n_rows, oproto, data);
731 release_data (n_rows, oproto, odata);
734 /* "destroy" function for struct mc_class. */
735 static void
736 datasheet_mc_destroy (const struct mc *mc UNUSED, void *ds_)
738 struct datasheet *ds = ds_;
739 datasheet_destroy (ds);
742 enum
744 OPT_MAX_ROWS,
745 OPT_MAX_COLUMNS,
746 OPT_BACKING_ROWS,
747 OPT_BACKING_WIDTHS,
748 OPT_WIDTHS,
749 OPT_HELP,
750 N_DATASHEET_OPTIONS
753 static const struct argv_option datasheet_argv_options[N_DATASHEET_OPTIONS] =
755 {"max-rows", 0, required_argument, OPT_MAX_ROWS},
756 {"max-columns", 0, required_argument, OPT_MAX_COLUMNS},
757 {"backing-rows", 0, required_argument, OPT_BACKING_ROWS},
758 {"backing-widths", 0, required_argument, OPT_BACKING_WIDTHS},
759 {"widths", 0, required_argument, OPT_WIDTHS},
760 {"help", 'h', no_argument, OPT_HELP},
763 static void usage (void);
765 static void
766 datasheet_option_callback (int id, void *params_)
768 struct datasheet_test_params *params = params_;
769 switch (id)
771 case OPT_MAX_ROWS:
772 params->max_rows = atoi (optarg);
773 break;
775 case OPT_MAX_COLUMNS:
776 params->max_cols = atoi (optarg);
777 break;
779 case OPT_BACKING_ROWS:
780 params->backing_rows = atoi (optarg);
781 break;
783 case OPT_BACKING_WIDTHS:
785 char *w;
787 params->n_backing_cols = 0;
788 for (w = strtok (optarg, ", "); w != NULL; w = strtok (NULL, ", "))
790 int value = atoi (w);
792 if (params->n_backing_cols >= MAX_COLS)
793 error (1, 0, "Too many widths on --backing-widths "
794 "(only %d are allowed)", MAX_COLS);
795 if (!isdigit (w[0]) || value < 0 || value > 31)
796 error (1, 0, "--backing-widths argument must be a list of 1 to "
797 "%d integers between 0 and 31 in increasing order",
798 MAX_COLS);
799 params->backing_widths[params->n_backing_cols++] = value;
802 break;
804 case OPT_WIDTHS:
806 int last = -1;
807 char *w;
809 params->n_widths = 0;
810 for (w = strtok (optarg, ", "); w != NULL; w = strtok (NULL, ", "))
812 int value = atoi (w);
814 if (params->n_widths >= MAX_WIDTHS)
815 error (1, 0, "Too many widths on --widths (only %d are allowed)",
816 MAX_WIDTHS);
817 if (!isdigit (w[0]) || value < 0 || value > 31)
818 error (1, 0, "--widths argument must be a list of 1 to %d "
819 "integers between 0 and 31 in increasing order",
820 MAX_WIDTHS);
822 /* This is an artificial requirement merely to ensure
823 that there are no duplicates. Duplicates aren't a
824 real problem but they would waste time. */
825 if (value <= last)
826 error (1, 0, "--widths arguments must be in increasing order");
828 params->widths[params->n_widths++] = value;
830 if (params->n_widths == 0)
831 error (1, 0, "at least one value must be specified on --widths");
833 break;
835 case OPT_HELP:
836 usage ();
837 break;
839 default:
840 NOT_REACHED ();
844 static void
845 usage (void)
847 printf ("%s, for testing the datasheet implementation.\n"
848 "Usage: %s [OPTION]...\n"
849 "\nTest state space parameters (min...max, default):\n"
850 " --max-rows=N Maximum number of rows (0...5, 3)\n"
851 " --max-rows=N Maximum number of columns (0...5, 3)\n"
852 " --backing-rows=N Rows of backing store (0...max_rows, 0)\n"
853 " --backing-widths=W[,W]... Backing store widths to test (0=num)\n"
854 " --widths=W[,W]... Column widths to test, where 0=numeric,\n"
855 " other values are string widths (0,1,11)\n",
856 program_name, program_name);
857 mc_options_usage ();
858 printf ("\nOther options:\n"
859 " --help Display this help message\n"
860 "\nReport bugs to <%s>\n", PACKAGE_BUGREPORT);
861 exit (0);
865 main (int argc, char *argv[])
867 static const struct mc_class datasheet_mc_class =
869 datasheet_mc_init,
870 datasheet_mc_mutate,
871 datasheet_mc_destroy,
874 struct datasheet_test_params params;
875 struct mc_options *options;
876 struct mc_results *results;
877 struct argv_parser *parser;
878 int verbosity;
879 bool success;
881 set_program_name (argv[0]);
883 /* Default parameters. */
884 params.max_rows = 3;
885 params.max_cols = 3;
886 params.backing_rows = 0;
887 params.n_backing_cols = 0;
888 params.widths[0] = 0;
889 params.widths[1] = 1;
890 params.widths[2] = 11;
891 params.n_widths = 3;
892 params.next_value = 1;
894 /* Parse command line. */
895 parser = argv_parser_create ();
896 options = mc_options_create ();
897 mc_options_register_argv_parser (options, parser);
898 argv_parser_add_options (parser, datasheet_argv_options, N_DATASHEET_OPTIONS,
899 datasheet_option_callback, &params);
900 if (!argv_parser_run (parser, argc, argv))
901 exit (EXIT_FAILURE);
902 argv_parser_destroy (parser);
903 verbosity = mc_options_get_verbosity (options);
905 /* Force parameters into allowed ranges. */
906 params.max_rows = MIN (params.max_rows, MAX_ROWS);
907 params.max_cols = MIN (params.max_cols, MAX_COLS);
908 params.backing_rows = MIN (params.backing_rows, params.max_rows);
909 params.n_backing_cols = MIN (params.n_backing_cols, params.max_cols);
910 mc_options_set_aux (options, &params);
911 results = mc_run (&datasheet_mc_class, options);
913 /* Output results. */
914 success = (mc_results_get_stop_reason (results) != MC_MAX_ERROR_COUNT
915 && mc_results_get_stop_reason (results) != MC_INTERRUPTED);
916 if (verbosity > 0 || !success)
918 int i;
920 printf ("Parameters: --max-rows=%d --max-columns=%d --backing-rows=%d ",
921 params.max_rows, params.max_cols, params.backing_rows);
923 printf ("--backing-widths=");
924 for (i = 0; i < params.n_backing_cols; i++)
926 if (i > 0)
927 printf (",");
928 printf ("%d", params.backing_widths[i]);
930 printf (" ");
932 printf ("--widths=");
933 for (i = 0; i < params.n_widths; i++)
935 if (i > 0)
936 printf (",");
937 printf ("%d", params.widths[i]);
939 printf ("\n\n");
940 mc_results_print (results, stdout);
942 mc_results_destroy (results);
944 return success ? 0 : EXIT_FAILURE;