/* PSPP - a program for statistical analysis.
Copyright (C) 2007, 2009, 2010, 2014 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 3 of the License, 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 . */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "error.h"
#include "minmax.h"
#include "progname.h"
/* lazy_casereader callback function to instantiate a casereader
from the datasheet. */
static struct casereader *
lazy_callback (void *ds_)
{
struct datasheet *ds = ds_;
return datasheet_make_reader (ds);
}
/* Maximum size of datasheet supported for model checking
purposes. */
#define MAX_ROWS 5
#define MAX_COLS 5
#define MAX_WIDTHS 5
/* Test params. */
struct datasheet_test_params
{
/* Parameters. */
int max_rows; /* Maximum number of rows. */
int max_cols; /* Maximum number of columns. */
int backing_rows; /* Number of rows of backing store. */
int backing_widths[MAX_COLS]; /* Widths of columns of backing store. */
int n_backing_cols; /* Number of columns of backing store. */
int widths[MAX_WIDTHS]; /* Allowed column widths. */
int n_widths;
/* State. */
unsigned int next_value;
};
static bool
check_caseproto (struct mc *mc, const struct caseproto *benchmark,
const struct caseproto *test, const char *test_name)
{
size_t n_columns = caseproto_get_n_widths (benchmark);
size_t col;
bool ok;
if (n_columns != caseproto_get_n_widths (test))
{
mc_error (mc, "%s column count (%zu) does not match expected (%zu)",
test_name, caseproto_get_n_widths (test), n_columns);
return false;
}
ok = true;
for (col = 0; col < n_columns; col++)
{
int benchmark_width = caseproto_get_width (benchmark, col);
int test_width = caseproto_get_width (test, col);
if (benchmark_width != test_width)
{
mc_error (mc, "%s column %zu width (%d) differs from expected (%d)",
test_name, col, test_width, benchmark_width);
ok = false;
}
}
return ok;
}
/* Checks that READER contains the N_ROWS rows and N_COLUMNS
columns of data in ARRAY, reporting any errors via MC. */
static void
check_datasheet_casereader (struct mc *mc, struct casereader *reader,
union value array[MAX_ROWS][MAX_COLS],
size_t n_rows, const struct caseproto *proto)
{
size_t n_columns = caseproto_get_n_widths (proto);
if (!check_caseproto (mc, proto, casereader_get_proto (reader),
"casereader"))
return;
else if (casereader_get_case_cnt (reader) != n_rows)
{
if (casereader_get_case_cnt (reader) == CASENUMBER_MAX
&& casereader_count_cases (reader) == n_rows)
mc_error (mc, "datasheet casereader has unknown case count");
else
mc_error (mc, "casereader row count (%lu) does not match "
"expected (%zu)",
(unsigned long int) casereader_get_case_cnt (reader),
n_rows);
}
else
{
struct ccase *c;
size_t row;
for (row = 0; row < n_rows; row++)
{
size_t col;
c = casereader_read (reader);
if (c == NULL)
{
mc_error (mc, "casereader_read failed reading row %zu of %zu "
"(%zu columns)", row, n_rows, n_columns);
return;
}
for (col = 0; col < n_columns; col++)
{
int width = caseproto_get_width (proto, col);
if (!value_equal (case_data_idx (c, col), &array[row][col],
width))
{
if (width == 0)
mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: "
"%.*g != %.*g",
row, col, n_rows, n_columns,
DBL_DIG + 1, case_num_idx (c, col),
DBL_DIG + 1, array[row][col].f);
else
mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: "
"'%.*s' != '%.*s'",
row, col, n_rows, n_columns,
width, case_str_idx (c, col),
width, array[row][col].s);
}
}
case_unref (c);
}
c = casereader_read (reader);
if (c != NULL)
mc_error (mc, "casereader has extra cases (expected %zu)", n_rows);
}
}
/* Checks that datasheet DS contains has N_ROWS rows, N_COLUMNS
columns, and the same contents as ARRAY, reporting any
mismatches via mc_error. Then, adds DS to MC as a new state. */
static void
check_datasheet (struct mc *mc, struct datasheet *ds,
union value array[MAX_ROWS][MAX_COLS],
size_t n_rows, const struct caseproto *proto)
{
size_t n_columns = caseproto_get_n_widths (proto);
struct datasheet *ds2;
struct casereader *reader;
unsigned long int serial = 0;
assert (n_rows < MAX_ROWS);
assert (n_columns < MAX_COLS);
/* Check contents of datasheet via datasheet functions. */
if (!check_caseproto (mc, proto, datasheet_get_proto (ds), "datasheet"))
{
/* check_caseproto emitted errors already. */
}
else if (n_rows != datasheet_get_n_rows (ds))
mc_error (mc, "row count (%lu) does not match expected (%zu)",
(unsigned long int) datasheet_get_n_rows (ds), n_rows);
else
{
size_t row, col;
bool difference = false;
for (row = 0; row < n_rows; row++)
for (col = 0; col < n_columns; col++)
{
int width = caseproto_get_width (proto, col);
union value *av = &array[row][col];
union value v;
value_init (&v, width);
if (!datasheet_get_value (ds, row, col, &v))
NOT_REACHED ();
if (!value_equal (&v, av, width))
{
if (width == 0)
mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: "
"%.*g != %.*g", row, col, n_rows, n_columns,
DBL_DIG + 1, v.f, DBL_DIG + 1, av->f);
else
mc_error (mc, "element %zu,%zu (of %zu,%zu) differs: "
"'%.*s' != '%.*s'",
row, col, n_rows, n_columns,
width, v.s,
width, v.s);
difference = true;
}
value_destroy (&v, width);
}
if (difference)
{
struct string s;
mc_error (mc, "expected:");
ds_init_empty (&s);
for (row = 0; row < n_rows; row++)
{
ds_clear (&s);
ds_put_format (&s, "row %zu:", row);
for (col = 0; col < n_columns; col++)
{
const union value *v = &array[row][col];
int width = caseproto_get_width (proto, col);
if (width == 0)
ds_put_format (&s, " %g", v->f);
else
ds_put_format (&s, " '%.*s'", width, v->s);
}
mc_error (mc, "%s", ds_cstr (&s));
}
mc_error (mc, "actual:");
ds_init_empty (&s);
for (row = 0; row < n_rows; row++)
{
ds_clear (&s);
ds_put_format (&s, "row %zu:", row);
for (col = 0; col < n_columns; col++)
{
int width = caseproto_get_width (proto, col);
union value v;
value_init (&v, width);
if (!datasheet_get_value (ds, row, col, &v))
NOT_REACHED ();
if (width == 0)
ds_put_format (&s, " %g", v.f);
else
ds_put_format (&s, " '%.*s'", width, v.s);
}
mc_error (mc, "%s", ds_cstr (&s));
}
ds_destroy (&s);
}
}
/* Check that datasheet contents are correct when read through
casereader. */
ds2 = clone_datasheet (ds);
reader = datasheet_make_reader (ds2);
check_datasheet_casereader (mc, reader, array, n_rows, proto);
casereader_destroy (reader);
/* Check that datasheet contents are correct when read through
casereader with lazy_casereader wrapped around it. This is
valuable because otherwise there is no non-GUI code that
uses the lazy_casereader. */
ds2 = clone_datasheet (ds);
reader = lazy_casereader_create (datasheet_get_proto (ds2), n_rows,
lazy_callback, ds2, &serial);
check_datasheet_casereader (mc, reader, array, n_rows, proto);
if (lazy_casereader_destroy (reader, serial))
{
/* Lazy casereader was never instantiated. This will
only happen if there are no rows (because in that case
casereader_read never gets called). */
datasheet_destroy (ds2);
if (n_rows != 0)
mc_error (mc, "lazy casereader not instantiated, but should "
"have been (size %zu,%zu)", n_rows, n_columns);
}
else
{
/* Lazy casereader was instantiated. This is the common
case, in which some casereader operation
(casereader_read in this case) was performed on the
lazy casereader. */
casereader_destroy (reader);
if (n_rows == 0)
mc_error (mc, "lazy casereader instantiated, but should not "
"have been (size %zu,%zu)", n_rows, n_columns);
}
if (mc_discard_dup_state (mc, hash_datasheet (ds)))
datasheet_destroy (ds);
else
mc_add_state (mc, ds);
}
/* Extracts the contents of DS into DATA. */
static void
extract_data (const struct datasheet *ds, union value data[MAX_ROWS][MAX_COLS])
{
const struct caseproto *proto = datasheet_get_proto (ds);
size_t n_columns = datasheet_get_n_columns (ds);
size_t n_rows = datasheet_get_n_rows (ds);
size_t row, col;
assert (n_rows < MAX_ROWS);
assert (n_columns < MAX_COLS);
for (row = 0; row < n_rows; row++)
for (col = 0; col < n_columns; col++)
{
int width = caseproto_get_width (proto, col);
union value *v = &data[row][col];
value_init (v, width);
if (!datasheet_get_value (ds, row, col, v))
NOT_REACHED ();
}
}
/* Copies the contents of ODATA into DATA. Each of the N_ROWS
rows of ODATA and DATA must have prototype PROTO. */
static void
clone_data (size_t n_rows, const struct caseproto *proto,
union value odata[MAX_ROWS][MAX_COLS],
union value data[MAX_ROWS][MAX_COLS])
{
size_t n_columns = caseproto_get_n_widths (proto);
size_t row, col;
assert (n_rows < MAX_ROWS);
assert (n_columns < MAX_COLS);
for (row = 0; row < n_rows; row++)
for (col = 0; col < n_columns; col++)
{
int width = caseproto_get_width (proto, col);
const union value *ov = &odata[row][col];
union value *v = &data[row][col];
value_init (v, width);
value_copy (v, ov, width);
}
}
static void
release_data (size_t n_rows, const struct caseproto *proto,
union value data[MAX_ROWS][MAX_COLS])
{
size_t n_columns = caseproto_get_n_widths (proto);
size_t row, col;
assert (n_rows < MAX_ROWS);
assert (n_columns < MAX_COLS);
for (col = 0; col < n_columns; col++)
{
int width = caseproto_get_width (proto, col);
if (value_needs_init (width))
for (row = 0; row < n_rows; row++)
value_destroy (&data[row][col], width);
}
}
/* Clones the structure and contents of ODS into *DS,
and the contents of ODATA into DATA. */
static void
clone_model (const struct datasheet *ods,
union value odata[MAX_ROWS][MAX_COLS],
struct datasheet **ds,
union value data[MAX_ROWS][MAX_COLS])
{
*ds = clone_datasheet (ods);
clone_data (datasheet_get_n_rows (ods), datasheet_get_proto (ods),
odata, data);
}
static void
value_from_param (union value *value, int width, unsigned int idx)
{
if (width == 0)
value->f = idx & 0xffff;
else
{
unsigned int hash = hash_int (idx, 0);
int offset;
assert (width < 32);
for (offset = 0; offset < width; offset++)
value->s[offset] = "ABCDEFGHIJ"[(hash >> offset) % 10];
}
}
/* "init" function for struct mc_class. */
static void
datasheet_mc_init (struct mc *mc)
{
struct datasheet_test_params *params = mc_get_aux (mc);
struct datasheet *ds;
if (params->backing_rows == 0 && params->n_backing_cols == 0)
{
/* Create unbacked datasheet. */
struct caseproto *proto;
ds = datasheet_create (NULL);
mc_name_operation (mc, "empty datasheet");
proto = caseproto_create ();
check_datasheet (mc, ds, NULL, 0, proto);
caseproto_unref (proto);
}
else
{
/* Create datasheet with backing. */
struct casewriter *writer;
struct casereader *reader;
union value data[MAX_ROWS][MAX_COLS];
struct caseproto *proto;
int row, col;
assert (params->backing_rows > 0 && params->backing_rows <= MAX_ROWS);
assert (params->n_backing_cols > 0
&& params->n_backing_cols <= MAX_COLS);
proto = caseproto_create ();
for (col = 0; col < params->n_backing_cols; col++)
proto = caseproto_add_width (proto, params->backing_widths[col]);
writer = mem_writer_create (proto);
for (row = 0; row < params->backing_rows; row++)
{
struct ccase *c;
c = case_create (proto);
for (col = 0; col < params->n_backing_cols; col++)
{
int width = params->backing_widths[col];
union value *value = &data[row][col];
value_init (value, width);
value_from_param (value, width, params->next_value++);
value_copy (case_data_rw_idx (c, col), value, width);
}
casewriter_write (writer, c);
}
reader = casewriter_make_reader (writer);
assert (reader != NULL);
ds = datasheet_create (reader);
mc_name_operation (mc, "datasheet with (%d,%d) backing",
params->backing_rows, params->n_backing_cols);
check_datasheet (mc, ds, data,
params->backing_rows, proto);
release_data (params->backing_rows, proto, data);
caseproto_unref (proto);
}
}
struct resize_cb_aux
{
int old_width;
int new_width;
};
static void
resize_cb (const union value *old_value, union value *new_value, const void *aux_)
{
const struct resize_cb_aux *aux = aux_;
value_from_param (new_value, aux->new_width,
value_hash (old_value, aux->old_width, 0));
}
/* "mutate" function for struct mc_class. */
static void
datasheet_mc_mutate (struct mc *mc, const void *ods_)
{
struct datasheet_test_params *params = mc_get_aux (mc);
const struct datasheet *ods = ods_;
union value odata[MAX_ROWS][MAX_COLS];
union value data[MAX_ROWS][MAX_COLS];
const struct caseproto *oproto = datasheet_get_proto (ods);
size_t n_columns = datasheet_get_n_columns (ods);
size_t n_rows = datasheet_get_n_rows (ods);
size_t pos, new_pos, cnt, width_idx;
extract_data (ods, odata);
/* Insert a column in each possible position. */
if (n_columns < params->max_cols)
for (pos = 0; pos <= n_columns; pos++)
for (width_idx = 0; width_idx < params->n_widths; width_idx++)
if (mc_include_state (mc))
{
int width = params->widths[width_idx];
struct caseproto *proto;
struct datasheet *ds;
union value new;
size_t i;
mc_name_operation (mc, "insert column at %zu "
"(from %zu to %zu columns)",
pos, n_columns, n_columns + 1);
clone_model (ods, odata, &ds, data);
value_init (&new, width);
value_from_param (&new, width, params->next_value++);
if (!datasheet_insert_column (ds, &new, width, pos))
mc_error (mc, "datasheet_insert_column failed");
proto = caseproto_insert_width (caseproto_ref (oproto),
pos, width);
for (i = 0; i < n_rows; i++)
{
insert_element (&data[i][0], n_columns, sizeof data[i][0],
pos);
value_init (&data[i][pos], width);
value_copy (&data[i][pos], &new, width);
}
value_destroy (&new, width);
check_datasheet (mc, ds, data, n_rows, proto);
release_data (n_rows, proto, data);
caseproto_unref (proto);
}
/* Resize each column to each possible new size. */
for (pos = 0; pos < n_columns; pos++)
for (width_idx = 0; width_idx < params->n_widths; width_idx++)
{
int owidth = caseproto_get_width (oproto, pos);
int width = params->widths[width_idx];
if (mc_include_state (mc))
{
struct resize_cb_aux aux;
struct caseproto *proto;
struct datasheet *ds;
size_t i;
mc_name_operation (mc, "resize column %zu (of %zu) "
"from width %d to %d",
pos, n_columns, owidth, width);
clone_model (ods, odata, &ds, data);
aux.old_width = owidth;
aux.new_width = width;
if (!datasheet_resize_column (ds, pos, width, resize_cb, &aux))
NOT_REACHED ();
proto = caseproto_set_width (caseproto_ref (oproto), pos, width);
for (i = 0; i < n_rows; i++)
{
union value *old_value = &data[i][pos];
union value new_value;
value_init (&new_value, width);
resize_cb (old_value, &new_value, &aux);
value_swap (old_value, &new_value);
value_destroy (&new_value, owidth);
}
check_datasheet (mc, ds, data, n_rows, proto);
release_data (n_rows, proto, data);
caseproto_unref (proto);
}
}
/* Delete all possible numbers of columns from all possible
positions. */
for (pos = 0; pos < n_columns; pos++)
for (cnt = 1; cnt < n_columns - pos; cnt++)
if (mc_include_state (mc))
{
struct caseproto *proto;
struct datasheet *ds;
size_t i, j;
mc_name_operation (mc, "delete %zu columns at %zu "
"(from %zu to %zu columns)",
cnt, pos, n_columns, n_columns - cnt);
clone_model (ods, odata, &ds, data);
datasheet_delete_columns (ds, pos, cnt);
proto = caseproto_remove_widths (caseproto_ref (oproto), pos, cnt);
for (i = 0; i < n_rows; i++)
{
for (j = pos; j < pos + cnt; j++)
value_destroy (&data[i][j], caseproto_get_width (oproto, j));
remove_range (&data[i], n_columns, sizeof *data[i], pos, cnt);
}
check_datasheet (mc, ds, data, n_rows, proto);
release_data (n_rows, proto, data);
caseproto_unref (proto);
}
/* Move all possible numbers of columns from all possible
existing positions to all possible new positions. */
for (pos = 0; pos < n_columns; pos++)
for (cnt = 1; cnt < n_columns - pos; cnt++)
for (new_pos = 0; new_pos < n_columns - cnt; new_pos++)
if (mc_include_state (mc))
{
struct caseproto *proto;
struct datasheet *ds;
size_t i;
clone_model (ods, odata, &ds, data);
mc_name_operation (mc, "move %zu columns (of %zu) from %zu to %zu",
cnt, n_columns, pos, new_pos);
datasheet_move_columns (ds, pos, new_pos, cnt);
for (i = 0; i < n_rows; i++)
move_range (&data[i], n_columns, sizeof data[i][0],
pos, new_pos, cnt);
proto = caseproto_move_widths (caseproto_ref (oproto),
pos, new_pos, cnt);
check_datasheet (mc, ds, data, n_rows, proto);
release_data (n_rows, proto, data);
caseproto_unref (proto);
}
/* Insert all possible numbers of rows in all possible
positions. */
for (pos = 0; pos <= n_rows; pos++)
for (cnt = 1; cnt <= params->max_rows - n_rows; cnt++)
if (mc_include_state (mc))
{
struct datasheet *ds;
struct ccase *c[MAX_ROWS];
size_t i, j;
clone_model (ods, odata, &ds, data);
mc_name_operation (mc, "insert %zu rows at %zu "
"(from %zu to %zu rows)",
cnt, pos, n_rows, n_rows + cnt);
for (i = 0; i < cnt; i++)
{
c[i] = case_create (oproto);
for (j = 0; j < n_columns; j++)
value_from_param (case_data_rw_idx (c[i], j),
caseproto_get_width (oproto, j),
params->next_value++);
}
insert_range (data, n_rows, sizeof data[pos], pos, cnt);
for (i = 0; i < cnt; i++)
for (j = 0; j < n_columns; j++)
{
int width = caseproto_get_width (oproto, j);
value_init (&data[i + pos][j], width);
value_copy (&data[i + pos][j], case_data_idx (c[i], j), width);
}
if (!datasheet_insert_rows (ds, pos, c, cnt))
mc_error (mc, "datasheet_insert_rows failed");
check_datasheet (mc, ds, data, n_rows + cnt, oproto);
release_data (n_rows + cnt, oproto, data);
}
/* Delete all possible numbers of rows from all possible
positions. */
for (pos = 0; pos < n_rows; pos++)
for (cnt = 1; cnt < n_rows - pos; cnt++)
if (mc_include_state (mc))
{
struct datasheet *ds;
clone_model (ods, odata, &ds, data);
mc_name_operation (mc, "delete %zu rows at %zu "
"(from %zu to %zu rows)",
cnt, pos, n_rows, n_rows - cnt);
datasheet_delete_rows (ds, pos, cnt);
release_data (cnt, oproto, &data[pos]);
remove_range (&data[0], n_rows, sizeof data[0], pos, cnt);
check_datasheet (mc, ds, data, n_rows - cnt, oproto);
release_data (n_rows - cnt, oproto, data);
}
/* Move all possible numbers of rows from all possible existing
positions to all possible new positions. */
for (pos = 0; pos < n_rows; pos++)
for (cnt = 1; cnt < n_rows - pos; cnt++)
for (new_pos = 0; new_pos < n_rows - cnt; new_pos++)
if (mc_include_state (mc))
{
struct datasheet *ds;
clone_model (ods, odata, &ds, data);
mc_name_operation (mc, "move %zu rows (of %zu) from %zu to %zu",
cnt, n_rows, pos, new_pos);
datasheet_move_rows (ds, pos, new_pos, cnt);
move_range (&data[0], n_rows, sizeof data[0],
pos, new_pos, cnt);
check_datasheet (mc, ds, data, n_rows, oproto);
release_data (n_rows, oproto, data);
}
release_data (n_rows, oproto, odata);
}
/* "destroy" function for struct mc_class. */
static void
datasheet_mc_destroy (const struct mc *mc UNUSED, void *ds_)
{
struct datasheet *ds = ds_;
datasheet_destroy (ds);
}
enum
{
OPT_MAX_ROWS,
OPT_MAX_COLUMNS,
OPT_BACKING_ROWS,
OPT_BACKING_WIDTHS,
OPT_WIDTHS,
OPT_HELP,
N_DATASHEET_OPTIONS
};
static const struct argv_option datasheet_argv_options[N_DATASHEET_OPTIONS] =
{
{"max-rows", 0, required_argument, OPT_MAX_ROWS},
{"max-columns", 0, required_argument, OPT_MAX_COLUMNS},
{"backing-rows", 0, required_argument, OPT_BACKING_ROWS},
{"backing-widths", 0, required_argument, OPT_BACKING_WIDTHS},
{"widths", 0, required_argument, OPT_WIDTHS},
{"help", 'h', no_argument, OPT_HELP},
};
static void usage (void);
static void
datasheet_option_callback (int id, void *params_)
{
struct datasheet_test_params *params = params_;
switch (id)
{
case OPT_MAX_ROWS:
params->max_rows = atoi (optarg);
break;
case OPT_MAX_COLUMNS:
params->max_cols = atoi (optarg);
break;
case OPT_BACKING_ROWS:
params->backing_rows = atoi (optarg);
break;
case OPT_BACKING_WIDTHS:
{
char *w;
params->n_backing_cols = 0;
for (w = strtok (optarg, ", "); w != NULL; w = strtok (NULL, ", "))
{
int value = atoi (w);
if (params->n_backing_cols >= MAX_COLS)
error (1, 0, "Too many widths on --backing-widths "
"(only %d are allowed)", MAX_COLS);
if (!isdigit (w[0]) || value < 0 || value > 31)
error (1, 0, "--backing-widths argument must be a list of 1 to "
"%d integers between 0 and 31 in increasing order",
MAX_COLS);
params->backing_widths[params->n_backing_cols++] = value;
}
}
break;
case OPT_WIDTHS:
{
int last = -1;
char *w;
params->n_widths = 0;
for (w = strtok (optarg, ", "); w != NULL; w = strtok (NULL, ", "))
{
int value = atoi (w);
if (params->n_widths >= MAX_WIDTHS)
error (1, 0, "Too many widths on --widths (only %d are allowed)",
MAX_WIDTHS);
if (!isdigit (w[0]) || value < 0 || value > 31)
error (1, 0, "--widths argument must be a list of 1 to %d "
"integers between 0 and 31 in increasing order",
MAX_WIDTHS);
/* This is an artificial requirement merely to ensure
that there are no duplicates. Duplicates aren't a
real problem but they would waste time. */
if (value <= last)
error (1, 0, "--widths arguments must be in increasing order");
params->widths[params->n_widths++] = value;
}
if (params->n_widths == 0)
error (1, 0, "at least one value must be specified on --widths");
}
break;
case OPT_HELP:
usage ();
break;
default:
NOT_REACHED ();
}
}
static void
usage (void)
{
printf ("%s, for testing the datasheet implementation.\n"
"Usage: %s [OPTION]...\n"
"\nTest state space parameters (min...max, default):\n"
" --max-rows=N Maximum number of rows (0...5, 3)\n"
" --max-rows=N Maximum number of columns (0...5, 3)\n"
" --backing-rows=N Rows of backing store (0...max_rows, 0)\n"
" --backing-widths=W[,W]... Backing store widths to test (0=num)\n"
" --widths=W[,W]... Column widths to test, where 0=numeric,\n"
" other values are string widths (0,1,11)\n",
program_name, program_name);
mc_options_usage ();
fputs ("\nOther options:\n"
" --help Display this help message\n"
"\nReport bugs to \n",
stdout);
exit (0);
}
int
main (int argc, char *argv[])
{
static const struct mc_class datasheet_mc_class =
{
datasheet_mc_init,
datasheet_mc_mutate,
datasheet_mc_destroy,
};
struct datasheet_test_params params;
struct mc_options *options;
struct mc_results *results;
struct argv_parser *parser;
int verbosity;
bool success;
set_program_name (argv[0]);
/* Default parameters. */
params.max_rows = 3;
params.max_cols = 3;
params.backing_rows = 0;
params.n_backing_cols = 0;
params.widths[0] = 0;
params.widths[1] = 1;
params.widths[2] = 11;
params.n_widths = 3;
params.next_value = 1;
/* Parse command line. */
parser = argv_parser_create ();
options = mc_options_create ();
mc_options_register_argv_parser (options, parser);
argv_parser_add_options (parser, datasheet_argv_options, N_DATASHEET_OPTIONS,
datasheet_option_callback, ¶ms);
if (!argv_parser_run (parser, argc, argv))
exit (EXIT_FAILURE);
argv_parser_destroy (parser);
verbosity = mc_options_get_verbosity (options);
/* Force parameters into allowed ranges. */
params.max_rows = MIN (params.max_rows, MAX_ROWS);
params.max_cols = MIN (params.max_cols, MAX_COLS);
params.backing_rows = MIN (params.backing_rows, params.max_rows);
params.n_backing_cols = MIN (params.n_backing_cols, params.max_cols);
mc_options_set_aux (options, ¶ms);
results = mc_run (&datasheet_mc_class, options);
/* Output results. */
success = (mc_results_get_stop_reason (results) != MC_MAX_ERROR_COUNT
&& mc_results_get_stop_reason (results) != MC_INTERRUPTED);
if (verbosity > 0 || !success)
{
int i;
printf ("Parameters: --max-rows=%d --max-columns=%d --backing-rows=%d ",
params.max_rows, params.max_cols, params.backing_rows);
printf ("--backing-widths=");
for (i = 0; i < params.n_backing_cols; i++)
{
if (i > 0)
printf (",");
printf ("%d", params.backing_widths[i]);
}
printf (" ");
printf ("--widths=");
for (i = 0; i < params.n_widths; i++)
{
if (i > 0)
printf (",");
printf ("%d", params.widths[i]);
}
printf ("\n\n");
mc_results_print (results, stdout);
}
mc_results_destroy (results);
return success ? 0 : EXIT_FAILURE;
}