#include <data/datasheet.h>
+#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <libpspp/taint.h>
#include <libpspp/tower.h>
+#include "error.h"
#include "minmax.h"
#include "progname.h"
#include "xalloc.h"
purposes. */
#define MAX_ROWS 5
#define MAX_COLS 5
+#define MAX_WIDTHS 5
/* Test params. */
struct datasheet_test_params
int max_cols; /* Maximum number of columns. */
int backing_rows; /* Number of rows of backing store. */
int backing_cols; /* Number of columns of backing store. */
+ int widths[MAX_WIDTHS]; /* Allowed column widths. */
+ int n_widths;
/* State. */
- int next_value;
+ unsigned int next_value;
};
static bool
assert (n_rows < MAX_ROWS);
assert (n_columns < MAX_COLS);
- /* If it is a duplicate hash, discard the state before checking
- its consistency, to save time. */
- if (mc_discard_dup_state (mc, hash_datasheet (ds)))
- {
- datasheet_destroy (ds);
- return;
- }
-
/* Check contents of datasheet via datasheet functions. */
if (!check_caseproto (mc, proto, datasheet_get_proto (ds), "datasheet"))
{
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, 0);
+ value_init (&v, width);
if (!datasheet_get_value (ds, row, col, &v))
NOT_REACHED ();
- ds_put_format (&s, " %g", v.f);
+ if (width == 0)
+ ds_put_format (&s, " %g", v.f);
+ else
+ ds_put_format (&s, " '%.*s'",
+ width, value_str (&v, width));
}
mc_error (mc, "%s", ds_cstr (&s));
}
"have been (size %zu,%zu)", n_rows, n_columns);
}
- mc_add_state (mc, ds);
+ 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
-value_from_param (union value *value, int width, int idx)
+value_from_param (union value *value, int width, unsigned int idx)
{
if (width == 0)
- value->f = idx;
+ value->f = idx & 0xffff;
else
{
unsigned int hash = hash_int (idx, 0);
}
}
+struct resize_cb_aux
+ {
+ int old_width;
+ int new_width;
+ };
+
+static void
+resize_cb (const union value *old_value, union value *new_value, void *aux_)
+{
+ 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);
- static const int widths[] = {0, 1, 11};
- const size_t n_widths = sizeof widths / sizeof *widths;
-
const struct datasheet *ods = ods_;
union value odata[MAX_ROWS][MAX_COLS];
union value data[MAX_ROWS][MAX_COLS];
/* 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 < n_widths; width_idx++)
+ for (width_idx = 0; width_idx < params->n_widths; width_idx++)
if (mc_include_state (mc))
{
- int width = widths[width_idx];
+ int width = params->widths[width_idx];
struct caseproto *proto;
struct datasheet *ds;
union value new;
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++)
OPT_MAX_COLUMNS,
OPT_BACKING_ROWS,
OPT_BACKING_COLUMNS,
+ OPT_WIDTHS,
OPT_HELP,
N_DATASHEET_OPTIONS
};
{"max-columns", 0, required_argument, OPT_MAX_COLUMNS},
{"backing-rows", 0, required_argument, OPT_BACKING_ROWS},
{"backing-columns", 0, required_argument, OPT_BACKING_COLUMNS},
+ {"widths", 0, required_argument, OPT_WIDTHS},
{"help", 'h', no_argument, OPT_HELP},
};
params->backing_cols = atoi (optarg);
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;
" --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-columns=N Columns of backing store (0...max_cols, 0)\n",
+ " --backing-columns=N Columns of backing store (0...max_cols, 0)\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"
params.max_cols = 3;
params.backing_rows = 0;
params.backing_cols = 0;
+ params.widths[0] = 0;
+ params.widths[1] = 1;
+ params.widths[2] = 11;
+ params.n_widths = 3;
params.next_value = 1;
/* Parse comand line. */
&& 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 --backing-columns=%d\n\n",
+ "--backing-rows=%d --backing-columns=%d ",
params.max_rows, params.max_cols,
params.backing_rows, params.backing_cols);
+ 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);