gnucash maint: Multiple changes pushed
John Ralls
jralls at code.gnucash.org
Mon Jul 16 17:08:39 EDT 2018
Updated via https://github.com/Gnucash/gnucash/commit/fa1b4c68 (commit)
via https://github.com/Gnucash/gnucash/commit/6f1c63db (commit)
via https://github.com/Gnucash/gnucash/commit/294e113f (commit)
via https://github.com/Gnucash/gnucash/commit/414ab99a (commit)
via https://github.com/Gnucash/gnucash/commit/b8ce2b54 (commit)
via https://github.com/Gnucash/gnucash/commit/aa4da810 (commit)
via https://github.com/Gnucash/gnucash/commit/dfe1f345 (commit)
via https://github.com/Gnucash/gnucash/commit/694d0f06 (commit)
via https://github.com/Gnucash/gnucash/commit/57c6f175 (commit)
via https://github.com/Gnucash/gnucash/commit/e2907844 (commit)
from https://github.com/Gnucash/gnucash/commit/c444729d (commit)
commit fa1b4c685f53c6da1090a9e0a6dee3ff6790d3b7
Author: John Ralls <jralls at ceridwen.us>
Date: Mon Jul 16 14:08:24 2018 -0700
Add jenny to sources for combinatorics testing.
diff --git a/borrowed/jenny/jenny.c b/borrowed/jenny/jenny.c
new file mode 100644
index 0000000..721b021
--- /dev/null
+++ b/borrowed/jenny/jenny.c
@@ -0,0 +1,1806 @@
+/*
+-------------------------------------------------------------------------------
+By Bob Jenkins, March 2003. Public domain.
+
+jenny.c -- jennyrate tests from m dimensions of features that cover all
+ n-tuples of features, n <= m, with each feature chosen from a different
+ dimension. For example, given 10 dimensions (m=10) with 2 to 8 features
+ apiece, cover all feature triplets (n=3). A lower bound on the number
+ of tests required is the product of the sizes of the largest n dimensions.
+ Already-written tests can be piped in to be reused.
+
+Arguments
+ Arguments without a leading '-' : an integer in 2..52. Represents a
+ dimension. Dimensions are implicitly numbered 1..65535, in the
+ order they appear. Features in dimensions are always implicitly
+ given 1-character names, which are in order a..z, A..Z . It's a
+ good idea to pass the output of jenny through a postprocessor that
+ expands these names into something intelligible.
+
+ -o : old. -ofoo.txt reads existing tests from the file foo.txt, includes
+ those tests in the output, and adds whatever other tests are needed to
+ complete coverage. An error is reported if the input tests are of the
+ wrong shape or contain disallowed feature interactions. If you have
+ added new dimensions since those tests were written, be sure to include
+ a do-nothing feature in each new dimension, then pad the existing tests
+ with do-nothing out to the correct number of dimensions.
+
+ -h : help. Print out instructions for using jenny.
+
+ -n : an integer. Cover all n-tuples of features, one from each dimension.
+ Default is 2 (pairs). 3 (triplets) may be reasonable. 4 (quadruplets)
+ is definitely overkill. n > 4 is highly discouraged.
+
+ -s : seed. An integer. Seed the random number generator.
+
+ -w : without this combination of features. A feature is given by a dimension
+ number followed by a one-character feature name. A single -w can
+ disallow multiple features in a dimension. For example, -w1a2cd4ac
+ disallows the combinations (1a,2c,4a),(1a,2c,4c),(1a,2d,4a),(1a,2d,4c)
+ where 1a represents the first dimension's first feature, 2c is the
+ second dimension's third feature, and 4a is the fourth dimension's
+ first feature.
+
+Example: 10 dimensions of 2, 3, 8, 3, 2, 2, 5, 3, 2, 2 features apiece,
+with some restrictions, asking for all triplets of features to be covered.
+This will produce at least 8*5*3=120 tests. Splitting the 8 features in the
+third dimension into three dimensions each of length 2 would reduce the
+number of testcases required to at least 5*3*3=45.
+
+ jenny -n3 2 3 8 -w1a2bc3b -w1b3a 3 -w1a4b 2 2 5 3 2 2 -w9a10b -w3a4b -s3
+-------------------------------------------------------------------------------
+*/
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+-------------------------------------------------------------------------------
+Implementation:
+
+Internally, there can be 64K dimensions with 64K features apiece. Externally,
+the number of features per dimensions is limited to just 52, and are implicitly
+named a..z, A..Z. Other printable characters, like |, caused trouble in the
+shell when I tried to give them during a without.
+
+The program first finds tests for all features, then adds tests to cover
+all pairs of features, then all triples of features, and so forth up to
+the tuples size the user asked for.
+-------------------------------------------------------------------------------
+*/
+
+/*
+-------------------------------------------------------------------------------
+Structures
+-------------------------------------------------------------------------------
+*/
+
+typedef unsigned char ub1;
+typedef char sb1;
+typedef unsigned short ub2;
+typedef signed short sb2;
+typedef unsigned long ub4;
+typedef signed long sb4;
+typedef unsigned long long ub8;
+typedef signed long long sb8;
+#define TRUE 1
+#define FALSE 0
+#define UB4MAXVAL 0xffffffff
+#define UB2MAXVAL 0xffff
+
+/*
+-------------------------------------------------------------------------------
+Random number stuff
+-------------------------------------------------------------------------------
+*/
+
+#define FLEARAND_SIZE 256
+typedef struct flearandctx {
+ ub4 b,c,d,z; /* special memory */
+ ub4 m[FLEARAND_SIZE]; /* big random pool of memory */
+ ub4 r[FLEARAND_SIZE]; /* results */
+ ub4 q; /* counter, which result of r was last reported */
+} flearandctx;
+
+/* Pseudorandom numbers, courtesy of FLEA */
+void flearand_batch( flearandctx *x) {
+ ub4 a, b=x->b, c=x->c+(++x->z), d=x->d, i, *m=x->m, *r=x->r;
+ for (i=0; i<FLEARAND_SIZE; ++i) {
+ a = m[b % FLEARAND_SIZE];
+ m[b % FLEARAND_SIZE] = d;
+ d = (c<<19) + (c>>13) + b;
+ c = b ^ m[i];
+ b = a + d;
+ r[i] = c;
+ }
+ x->b=b; x->c=c; x->d=d;
+}
+
+ub4 flearand( flearandctx *x) {
+ if (!x->q--) {
+ x->q = FLEARAND_SIZE-1;
+ flearand_batch(x);
+ }
+ return x->r[x->q];
+}
+
+void flearand_init( flearandctx *x, ub4 seed) {
+ ub4 i;
+
+ x->b = x->c = x->d = x->z = seed;
+ for (i = 0; i<FLEARAND_SIZE; ++i) {
+ x->m[i] = seed;
+ }
+ for (i=0; i<10; ++i) {
+ flearand_batch(x);
+ }
+ x->q = 0;
+}
+
+
+
+
+/*
+------------------------------------------------------------------------------
+Other helper routines
+------------------------------------------------------------------------------
+*/
+
+#define TUPLE_ARRAY 5040 /* tuple array size, multiple of 1,2,3,4,5,6 */
+
+/* An arbitrary feature, prefix fe */
+typedef struct feature {
+ ub2 d; /* Dimension name */
+ ub2 f; /* Feature name */
+} feature;
+
+/* a tuple array, prefix tu */
+typedef struct tu_arr {
+ struct tu_arr *next; /* next tuple array */
+ ub2 len; /* length of this array */
+ feature fe[TUPLE_ARRAY]; /* array of tuples */
+} tu_arr;
+
+/* an iterator over a tuple array, prefix tu */
+typedef struct tu_iter {
+ struct tu_arr **tu; /* current tuple array */
+ ub2 offset; /* offset of current tuple */
+ ub2 n; /* number of features per tuple */
+ ub4 *count; /* number of tuples in list */
+ feature *fe; /* current tuple */
+} tu_iter;
+
+/* names of features, for output */
+static const char feature_name[] =
+"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+
+
+/*
+------------------------------------------------------------------------------
+Stuff specific to jenny
+------------------------------------------------------------------------------
+*/
+
+#define MAX_FEATURES 52 /* can't name more than 52 features */
+#define MAX_TESTS 65534 /* arbitrary limit on number of testcases */
+#define MAX_N 32 /* never can do complete coverage of 33-tuples */
+#define MAX_WITHOUT (MAX_FEATURES*MAX_N) /* max features in a without */
+#define MAX_DIMENSIONS (((ub2)~0)-1) /* More than 64K dimensions needs a ub4 */
+
+/* A "test", which is a combination of features. Prefix t. */
+typedef struct test {
+ ub2 *f; /* features in this test */
+} test;
+
+/* representation of a restriction, prefix w */
+typedef struct without {
+ ub2 len; /* length of feature array */
+ struct feature *fe; /* feature array */
+} without;
+
+/* without chain */
+typedef struct wchain {
+ without *w;
+ struct wchain *next;
+} wchain;
+
+
+/* Return a count of how many withouts are disobeyed. */
+/* Also set a pointer to a randomly chosen violated without */
+int count_withouts(
+test *t, /* test to check */
+wchain *wc) /* restrictions */
+{
+ ub4 count; /* count of disobeyed withouts */
+
+ for (count = 0; wc; wc = wc->next) {
+ without *w = wc->w;
+ ub4 i = 0;
+ int match = TRUE; /* match the entire restriction */
+ while (i<w->len) {
+ int dimension_match = FALSE; /* match in this dimension */
+ do {
+ if (t->f[w->fe[i].d] == w->fe[i].f) {
+ dimension_match = TRUE;
+ }
+ ++i;
+ } while (i<w->len && (w->fe[i].d == w->fe[i-1].d));
+ if (!dimension_match) {
+ match = FALSE;
+ break;
+ }
+ }
+ if (match) {
+ ++count;
+ }
+ }
+ return count;
+}
+
+
+/* token definitions */
+typedef enum token_type {
+ TOKEN_ERROR = 0, /* TOKEN_ERROR has to be 0 */
+ TOKEN_END,
+ TOKEN_NUMBER,
+ TOKEN_FEATURE,
+ TOKEN_SPACE
+} token_type;
+
+
+/* whole current state, prefix s */
+typedef struct state {
+ ub1 n_final; /* The n in the user's n-tuples, default is 2 */
+ ub2 ndim; /* number of dimensions */
+ ub2 ntests; /* number of testcases in t */
+ ub1 **n; /* n[d][f] is current n-tuple size for dimension d feature f */
+ test **t; /* all the tests generated so far */
+ ub2 *dim; /* number of features in each dimension */
+ wchain **wc; /* s->wc[d] lists withouts for dimension d */
+ wchain *wc2; /* a list of all the original withouts */
+ wchain *wc3; /* additional, deduced withouts */
+ tu_arr ***tu; /* tu[d][f] lists untested tuples for dimension d feature f */
+ tu_arr ***one;
+ /* one[testcase][d] lists tuples with d covered only by this testcase */
+ ub4 **onec; /* onec[testcase][d] is count of one[testcase][d] */
+ ub4 **used;
+ /* used[testcase][d] = pass# if this pass has already explored test[t][d] */
+ ub4 **tc; /* tc[d][f] is # untested tulpes for dimension d feature f */
+ test *tuple_tester; /* an all -1 test used to test individual tuples */
+ ub2 *dimord; /* order in which to choose dimensions */
+ ub2 *featord; /* order in which to choose features */
+ flearandctx r; /* random number context */
+} state;
+
+
+void my_free( char *x)
+{
+ free(x);
+}
+
+/* zero out a list of tuples */
+void truncate_tuple( tu_arr **tu, ub4 *count)
+{
+ while (*tu) {
+ tu_arr *tu2 = *tu;
+ *tu = ((*tu)->next);
+ my_free((char *)tu2);
+ }
+ *count = 0;
+}
+
+/* delete the i-th test */
+void delete_test( state *s, ub4 i)
+{
+ test *t = s->t[i];
+
+ s->t[i] = s->t[--s->ntests];
+ my_free((char *)t->f);
+ my_free((char *)t);
+ if (s->one[s->ntests]) {
+ ub2 d;
+ for (d=0; d<s->ndim; ++d) {
+ truncate_tuple(&s->one[s->ntests][d], &s->onec[s->ntests][d]);
+ }
+ my_free((char *)s->one[s->ntests]);
+ my_free((char *)s->onec[s->ntests]);
+ s->one[s->ntests] = (tu_arr **)0;
+ s->onec[s->ntests] = (ub4 *)0;
+ }
+}
+
+void cleanup(state *s)
+{
+ if (s->tu) {
+ ub2 d,f;
+ for (d=0; d<s->ndim; ++d) {
+ if (s->tu[d]) {
+ for (f=0; f<s->dim[d]; ++f) {
+ truncate_tuple(&s->tu[d][f], &s->tc[d][f]);
+ }
+ my_free((char *)s->tu[d]);
+ }
+ }
+ my_free((char *)s->tu);
+ }
+
+ /* free n, the tuple lengths */
+ if (s->n) {
+ ub2 d;
+ for (d=0; d<s->ndim; ++d) {
+ if (s->n[d]) {
+ my_free((char *)s->n[d]);
+ }
+ }
+ my_free((char *)s->n);
+ }
+
+ /* free tc, count of uncovered tuples */
+ if (s->tc) {
+ ub2 d;
+ for (d=0; d<s->ndim; ++d) {
+ if (s->tc[d]) {
+ my_free((char *)s->tc[d]);
+ }
+ }
+ my_free((char *)s->tc);
+ }
+
+ /* free the secondary chains of restrictions */
+ if (s->wc) {
+ ub2 i;
+ for (i=0; i<s->ndim; ++i) {
+ while (s->wc[i]) {
+ wchain *wc = s->wc[i];
+ s->wc[i] = s->wc[i]->next;
+ my_free((char *)wc);
+ }
+ }
+ my_free((char *)s->wc);
+ }
+
+ /* free all the actual restrictions */
+ while (s->wc2) {
+ wchain *wc = s->wc2;
+ without *w = wc->w;
+ s->wc2 = s->wc2->next;
+ if (w->fe) my_free((char *)w->fe);
+ my_free((char *)w);
+ my_free((char *)wc);
+ }
+
+ while (s->wc3) {
+ wchain *wc = s->wc3;
+ without *w = wc->w;
+ s->wc3 = s->wc3->next;
+ if (w->fe) my_free((char *)w->fe);
+ my_free((char *)w);
+ my_free((char *)wc);
+ }
+
+ if (s->t) {
+ while (s->ntests) {
+ delete_test(s, 0);
+ }
+ my_free((char *)s->t);
+ }
+
+ if (s->one) {
+ my_free((char *)s->one);
+ }
+
+ if (s->onec) {
+ my_free((char *)s->onec);
+ }
+
+ if (s->tuple_tester) {
+ if (s->tuple_tester->f) {
+ my_free((char *)s->tuple_tester->f);
+ }
+ my_free((char *)s->tuple_tester);
+ }
+
+ if (s->dimord) {
+ my_free((char *)s->dimord);
+ }
+
+ if (s->featord) {
+ my_free((char *)s->featord);
+ }
+
+ /* free the array of dimension lengths */
+ if (s->dim) my_free((char *)s->dim);
+}
+
+
+char *my_alloc( state *s, size_t len)
+{
+ char *rsl;
+ if (!(rsl = (char *)malloc(len+sizeof(size_t)))) {
+ printf("jenny: could not allocate space\n");
+ cleanup(s);
+ exit(0);
+ }
+ memset(rsl, 0x00, len);
+ return rsl;
+}
+
+/* insert a tuple into a tuple array */
+int insert_tuple( state *s, tu_iter *ctx, feature *tuple)
+{
+ ub4 i;
+ feature *fe;
+ ub1 n = ctx->n;
+ ub4 lim = TUPLE_ARRAY / n;
+
+ while (*ctx->tu && (*ctx->tu)->len == lim) {
+ ctx->tu = &((*ctx->tu)->next);
+ }
+ if (!*ctx->tu) {
+ if (!((*ctx->tu) = (tu_arr *)my_alloc(s, sizeof(tu_arr)))) {
+ return FALSE;
+ }
+ (*ctx->tu)->len = 0;
+ (*ctx->tu)->next = (tu_arr *)0;
+ }
+ fe = &(*ctx->tu)->fe[(*ctx->tu)->len*n];
+ for (i=0; i<n; ++i) {
+ fe[i].d = tuple[i].d;
+ fe[i].f = tuple[i].f;
+ }
+ ++(*ctx->tu)->len;
+ ++*ctx->count;
+ return TRUE;
+}
+
+/* print out a single tuple */
+void show_tuple( feature *fe, ub2 len)
+{
+ ub4 i;
+ for (i=0; i<len; ++i) {
+ printf(" %d%c", fe[i].d+1, feature_name[fe[i].f]);
+ }
+ printf(" \n");
+}
+
+/* delete a tuple from a tuple array */
+feature *delete_tuple( tu_iter *ctx)
+{
+ feature *fe;
+ ub4 i;
+ feature *tuple = ctx->fe;
+ ub1 n = ctx->n;
+
+ --(*ctx->tu)->len;
+ --*ctx->count;
+ fe = &(*ctx->tu)->fe[(*ctx->tu)->len * n];
+ for (i=0; i<n; ++i) {
+ tuple[i].d = fe[i].d;
+ tuple[i].f = fe[i].f;
+ }
+ if (!(*ctx->tu)->len) {
+ tu_arr *tu2 = *ctx->tu;
+ *ctx->tu = ((*ctx->tu)->next);
+ my_free((char *)tu2);
+ }
+
+ if (!*ctx->tu) { /* freed the last block */
+ ctx->offset = 0;
+ ctx->fe = (feature *)0;
+ return ctx->fe;
+ } else if (tuple == fe) {
+ ctx->offset = 0;
+ ctx->fe = &(*ctx->tu)->fe[0]; /* freed this block, move to next */
+ return ctx->fe;
+ } else {
+ return tuple; /* moved a new tuple into the old location */
+ }
+}
+
+/* start a tuple iterator */
+feature *start_tuple( tu_iter *ctx, tu_arr **tu, ub4 n, ub4 *count)
+{
+ ctx->tu = tu;
+ ctx->offset = 0;
+ ctx->n = n;
+ ctx->count = count;
+
+ if (*tu) {
+ ctx->fe = (*tu)->fe;
+ } else {
+ ctx->fe = (feature *)0;
+ }
+ return ctx->fe;
+}
+
+/* get the next tuple from a tuple iterator (0 if no more tuples) */
+static feature *next_tuple( tu_iter *ctx)
+{
+ if (++ctx->offset < (*ctx->tu)->len) {
+ ctx->fe += ctx->n;
+ } else {
+ ctx->tu = &(*ctx->tu)->next;
+ ctx->offset = 0;
+ if (*ctx->tu && (*ctx->tu)->len) {
+ ctx->fe = (*ctx->tu)->fe;
+ } else {
+ ctx->fe = (feature *)0;
+ }
+ }
+ return ctx->fe;
+}
+
+
+/* test if this test covers this tuple */
+static int test_tuple( ub2 *test, feature *tuple, ub2 n)
+{
+ sb4 i;
+ for (i=0; i<n; ++i) {
+ if (tuple[i].f != test[tuple[i].d]) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/* test if first tuple (t1, n1) is a subset of second tuple (t2, n2) */
+int subset_tuple( feature *t1, ub1 n1, feature *t2, ub1 n2)
+{
+ sb4 i, j;
+ if (n2 < n1)
+ return FALSE;
+ for (i=0, j=0; i<n1; ++i) {
+ while (t1[i].d > t2[j].d) {
+ if (++j == n2)
+ return FALSE;
+ }
+ if (t1[i].d != t2[j].d || t1[i].f != t2[j].f) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+void initialize( state *s)
+{
+ /* make all freeable pointers start out zero */
+ s->dim = (ub2 *)0;
+ s->wc = (wchain **)0;
+ s->wc2 = (wchain *)0;
+ s->wc3 = (wchain *)0;
+ s->tu = (tu_arr ***)0;
+ s->one = (tu_arr ***)0;
+ s->onec = (ub4 **)0;
+ s->n = (ub1 **)0;
+ s->tc = (ub4 **)0;
+ s->t = (test **)0;
+ s->tuple_tester = (test *)0;
+ s->dimord = (ub2 *)0;
+ s->featord = (ub2 *)0;
+
+ /* fill in default values */
+ s->ndim = (ub2)0;
+ s->n_final = 2; /* guarantees that all pairs of dimensions are covered */
+ s->ntests = 0;
+ flearand_init(&s->r, 0); /* initialize random number generator */
+}
+
+
+/* add one test to the list of tests */
+int add_test( state *s, test *t)
+{
+ ub4 i;
+ if (s->ntests == MAX_TESTS) {
+ return FALSE;
+ }
+ s->one[s->ntests] = (tu_arr **)my_alloc(s, sizeof(tu_arr *)*s->ndim);
+ s->onec[s->ntests] = (ub4 *)my_alloc(s, sizeof(ub4)*s->ndim);
+ for (i=0; i<s->ndim; ++i) {
+ s->one[s->ntests][i] = (tu_arr *)0;
+ s->onec[s->ntests][i] = 0;
+ }
+ s->t[s->ntests++] = t;
+ return TRUE;
+}
+
+/*
+ * parse a token
+ * Start at *curr in string *inp of length inl
+ * Adjust *curr to be after the just-parsed token
+ * Place the token value in *rsl
+ * Return the token type
+ */
+token_type parse_token(char *inp, ub4 inl, ub4 *curr, ub4 *rsl)
+{
+ char mychar;
+ ub4 i;
+
+ if (*curr == inl)
+ return TOKEN_END;
+ mychar = inp[*curr];
+
+ if (mychar == '\0') {
+ return TOKEN_END;
+ } else if (mychar == ' ' || mychar == '\t' || mychar == '\n') {
+ /*--------------------------------------------------------- parse spaces */
+ for (i=*curr+1; i < inl; ++i) {
+ mychar = inp[i];
+ if (!(mychar == ' ' || mychar == '\t' || mychar == '\n'))
+ break;
+ }
+ *curr = i;
+ return TOKEN_SPACE;
+ } else if (mychar >= '0' && mychar <= '9') {
+ /*------------------------------------------------------- parse a number */
+ ub4 i, number = 0;
+ for (i=*curr; i < inl && inp[i] >= '0' && inp[i] <= '9'; ++i) {
+ number = number*10 + (inp[i] - '0');
+ }
+ *curr = i;
+ *rsl = number;
+ return TOKEN_NUMBER;
+ } else if ((mychar >= 'a' && mychar <= 'z') ||
+ (mychar >= 'A' && mychar <= 'Z')) {
+ /*------------------------------------------------- parse a feature name */
+ ub4 i;
+ for (i=0; i<MAX_FEATURES; ++i)
+ if (feature_name[i] == mychar)
+ break;
+ if (i == MAX_FEATURES) {
+ printf("jenny: the name '%c' is not used for any feature\n",
+ mychar);
+ return TOKEN_ERROR;
+ }
+ *rsl = i;
+ ++*curr;
+ return TOKEN_FEATURE;
+ } else {
+ return TOKEN_ERROR;
+ }
+}
+
+#define BUFSIZE (MAX_DIMENSIONS*7+2)
+/* load old tests before generating new ones */
+int load( state *s, char *testfile)
+{
+ char buf[BUFSIZE]; /* buffer holding a line read from the file */
+ FILE *f;
+
+ if (testfile[0] == '\0') {
+ f = stdin;
+ } else {
+ f = fopen(testfile, "r");
+ }
+
+ if (!f) {
+ printf("jenny: file %s could not be opened\n", testfile);
+ return FALSE;
+ }
+
+ while (fgets(buf, BUFSIZE, f) && (buf[0] != '.')) {
+ ub4 curr = 0; /* current offset into buf */
+ ub4 value; /* token value */
+ token_type token; /* token type */
+ ub4 i;
+ test *t;
+
+ t = (test *)my_alloc( s, sizeof(test));
+ t->f = (ub2 *)my_alloc( s, sizeof(ub2)*s->ndim);
+ if (!add_test(s, t)) {
+ goto failure;
+ }
+
+ for (i=0; i<s->ndim; ++i) {
+ if (parse_token(buf, UB4MAXVAL, &curr, &value) != TOKEN_SPACE) {
+ printf("jenny: -o, non-space found where space expected\n");
+ goto failure;
+ }
+ if (parse_token(buf, UB4MAXVAL, &curr, &value) != TOKEN_NUMBER) {
+ printf("jenny: -o, non-number found where number expected\n");
+ goto failure;
+ }
+ if (value-1 != i) {
+ printf("jenny: -o, number %d found out-of-place\n", value);
+ goto failure;
+ }
+ if (parse_token(buf, UB4MAXVAL, &curr, &value) != TOKEN_FEATURE) {
+ printf("jenny: -o, non-feature found where feature expected\n");
+ goto failure;
+ }
+ if (value >= s->dim[i]) {
+ printf("jenny: -o, feature %c does not exist in dimension %d\n",
+ feature_name[value], i+1);
+ goto failure;
+ }
+ t->f[i] = value;
+ }
+ if (parse_token(buf, UB4MAXVAL, &curr, &value) != TOKEN_SPACE) {
+ printf("jenny: -o, non-space found where trailing space expected\n");
+ goto failure;
+ }
+ if (parse_token(buf, UB4MAXVAL, &curr, &value) != TOKEN_END) {
+ printf("jenny: -o, testcase not properly terminated\n");
+ goto failure;
+ }
+
+ /* make sure the testcase obeys all the withouts */
+ if (count_withouts(t, s->wc2)) {
+ printf("jenny: -o, old testcase contains some without\n");
+ goto failure;
+ }
+ }
+
+ (void)fclose(f);
+ return TRUE;
+
+ failure:
+ while (fgets(buf, BUFSIZE, f) && (buf[0] != '.'))
+ ; /* finish reading the input */
+ (void)fclose(f); /* close the file */
+ return FALSE;
+}
+
+static const sb1 *jenny_doc[] = {
+ "jenny:\n",
+ " Given a set of feature dimensions and withouts, produce tests\n",
+ " covering all n-tuples of features where all features come from\n",
+ " different dimensions. For example (=, <, >, <=, >=, !=) is a\n",
+ " dimension with 6 features. The type of the left-hand argument is\n",
+ " another dimension. Dimensions are numbered 1..65535, in the order\n",
+ " they are listed. Features are implicitly named a..z, A..Z.\n",
+ " 3 Dimensions are given by the number of features in that dimension.\n",
+ " -h prints out these instructions.\n",
+ " -n specifies the n in n-tuple. The default is 2 (meaning pairs).\n",
+ " -w gives withouts. -w1b4ab says that combining the second feature\n",
+ " of the first dimension with the first or second feature of the\n",
+ " fourth dimension is disallowed.\n",
+ " -ofoo.txt reads old jenny testcases from file foo.txt and extends them.",
+ "\n\n",
+ " The output is a testcase per line, one feature per dimension per\n",
+ " testcase, followed by the list of all allowed tuples that jenny could\n",
+ " not reach.\n",
+ "\n",
+ " Example: jenny -n3 3 2 2 -w2b3b 5 3 -w1c3b4ace5ac 8 2 2 3 2\n",
+ " This gives ten dimensions, asks that for any three dimensions all\n",
+ " combinations of features (one feature per dimension) be covered,\n",
+ " plus it asks that certain combinations of features\n",
+ " (like (1c,3b,4c,5c)) not be covered.\n",
+ "\n"
+};
+
+
+/* parse -n, the tuple size */
+int parse_n( state *s, char *myarg)
+{
+ ub4 curr = 0;
+ ub4 temp = UB4MAXVAL;
+ token_type token;
+ ub4 dummy;
+
+ if ((token=parse_token(myarg, UB4MAXVAL, &curr, &temp)) != TOKEN_NUMBER) {
+ printf("jenny: -n should give an integer in 1..32, for example, -n2.\n");
+ return FALSE;
+ }
+ if ((token=parse_token(myarg, UB4MAXVAL, &curr, &dummy)) != TOKEN_END) {
+ printf("jenny: -n should be followed by just an integer\n");
+ return FALSE;
+ }
+
+ if ((temp < 1) || (temp > 32)) {
+ printf("jenny: -n says all n-tuples should be covered.\n");
+ return FALSE;
+ }
+ if (temp > s->ndim) {
+ printf("jenny: -n, %ld-tuples are impossible with only %d dimensions\n",
+ temp, s->ndim);
+ return FALSE;
+ }
+ s->n_final = (ub2)temp;
+ return TRUE;
+}
+
+
+
+/* parse -w, a without */
+int parse_w( state *s, sb1 *myarg)
+{
+ without *w;
+ wchain *wc;
+ feature fe[MAX_WITHOUT];
+ ub1 used[MAX_DIMENSIONS];
+ ub4 dimension_number;
+ ub4 curr = 0;
+ ub4 fe_len, value;
+ ub4 i, j, k;
+ size_t len = strlen(myarg);
+ token_type t = parse_token(myarg, len, &curr, &value);
+
+ for (i=0; i<s->ndim; ++i)
+ used[i] = FALSE;
+ if (t != TOKEN_NUMBER) {
+ printf("jenny: -w is <number><features><number><features>...\n");
+ printf("jenny: -w must start with an integer (1 to #dimensions)\n");
+ return FALSE;
+ }
+ fe_len=0;
+
+ number:
+ dimension_number = --value;
+ if (dimension_number >= s->ndim) {
+ printf("jenny: -w, dimension %ld does not exist, ", dimension_number+1);
+ printf("you gave only %d dimensions\n", s->ndim);
+ return FALSE;
+ }
+ if (used[dimension_number]) {
+ printf("jenny: -w, dimension %d was given twice in a single without\n",
+ dimension_number+1);
+ return FALSE;
+ }
+ used[dimension_number] = TRUE;
+
+
+ switch (parse_token(myarg, len, &curr, &value)) {
+ case TOKEN_FEATURE: goto feature;
+ case TOKEN_END:
+ printf("jenny: -w, withouts must follow numbers with features\n");
+ return FALSE;
+ default:
+ printf("jenny: -w, unexpected without syntax\n");
+ printf("jenny: proper withouts look like -w2a1bc99a\n");
+ return FALSE;
+ }
+
+ feature:
+ if (value >= s->dim[dimension_number]) {
+ printf("jenny: -w, there is no feature '%c' in dimension %d\n",
+ feature_name[value], dimension_number+1);
+ return FALSE;
+ }
+ fe[fe_len].d = dimension_number;
+ fe[fe_len].f = value;
+ if (++fe_len >= MAX_WITHOUT) {
+ printf("jenny: -w, at most %d features in a single without\n",
+ MAX_WITHOUT);
+ return FALSE;
+ }
+
+
+ switch (parse_token(myarg, len, &curr, &value)) {
+ case TOKEN_FEATURE: goto feature;
+ case TOKEN_NUMBER: goto number;
+ case TOKEN_END: goto end;
+ default:
+ printf("jenny: -w, unexpected without syntax\n");
+ printf("jenny: proper withouts look like -w2a1bc99a\n");
+ return FALSE;
+ }
+
+ end:
+
+ /* sort the dimensions and features in this restriction */
+ for (i=0; i<fe_len; ++i) {
+ for (j=i+1; j<fe_len; ++j) {
+ if ((fe[i].d > fe[j].d) ||
+ ((fe[i].d == fe[j].d) && (fe[i].f > fe[j].f))) {
+ ub2 fe_temp;
+ fe_temp = fe[i].d;
+ fe[i].d = fe[j].d;
+ fe[j].d = fe_temp;
+ fe_temp = fe[i].f;
+ fe[i].f = fe[j].f;
+ fe[j].f = fe_temp;
+ }
+ }
+ }
+
+ /* allocate a without */
+ w = (without *)my_alloc( s, sizeof(without));
+ wc = (wchain *)my_alloc( s, sizeof(wchain));
+ wc->next = s->wc2;
+ wc->w = w;
+ w->len = fe_len;
+ w->fe = (feature *)my_alloc( s, sizeof(feature)*fe_len);
+ for (i=0; i<fe_len; ++i) {
+ w->fe[i].d = fe[i].d;
+ w->fe[i].f = fe[i].f;
+ }
+ s->wc2 = wc;
+
+ return TRUE;
+}
+
+/* parse -s, a seed for the random number generator */
+int parse_s( state *s, sb1 *myarg)
+{
+ ub4 seed = 0;
+ ub4 dummy = 0;
+ ub4 curr = 0;
+ if (parse_token( myarg, UB4MAXVAL, &curr, &seed) != TOKEN_NUMBER) {
+ printf("jenny: -s must be followed by a positive integer\n");
+ return FALSE;
+ }
+ if (parse_token( myarg, UB4MAXVAL, &curr, &dummy) != TOKEN_END) {
+ printf("jenny: -s should give just an integer, example -s123\n");
+ return FALSE;
+ }
+ flearand_init(&s->r, seed); /* initialize random number generator */
+ return TRUE;
+}
+
+void preliminary( state *s)
+{
+ wchain *wc;
+ ub4 d;
+
+ s->tuple_tester = (test *)my_alloc( s, sizeof(test));
+ s->tuple_tester->f = (ub2 *)my_alloc( s, sizeof(ub2)*s->ndim);
+ s->dimord = (ub2 *)my_alloc( s, sizeof(ub2)*s->ndim);
+ s->wc = (wchain **)my_alloc( s, sizeof(wchain *)*s->ndim);
+ s->tu = (tu_arr ***)my_alloc( s, sizeof(tu_arr **)*s->ndim);
+ s->one = (tu_arr ***)my_alloc( s, sizeof(tu_arr **)*MAX_TESTS);
+ s->n = (ub1 **)my_alloc( s, sizeof(ub1 *)*s->ndim);
+ s->onec = (ub4 **)my_alloc( s, sizeof(ub4 *)*MAX_TESTS);
+ s->tc = (ub4 **)my_alloc( s, sizeof(ub4 *)*s->ndim);
+ s->t = (test **)my_alloc( s, sizeof(test *)*MAX_TESTS);
+
+ /* initialize to safe values before doing further allocations */
+ for (d=0; d<s->ndim; ++d) {
+ s->tuple_tester->f[d] = (ub2)~0;
+ s->dimord[d] = (ub2)d;
+ s->wc[d] = (wchain *)0;
+ s->tu[d] = (tu_arr **)0;
+ s->n[d] = (ub1 *)0;
+ s->tc[d] = (ub4 *)0;
+ }
+
+ s->featord = (ub2 *)my_alloc( s, sizeof(ub2)*MAX_FEATURES);
+
+ /* allocate roots for feature-specific lists of uncovered tuples */
+ for (d=0; d<s->ndim; ++d) {
+ ub2 f;
+ s->tu[d] = (tu_arr **)my_alloc( s, sizeof(tu_arr *)*s->dim[d]);
+ s->n[d] = (ub1 *)my_alloc(s, sizeof(ub1)*s->dim[d]);
+ s->tc[d] = (ub4 *)my_alloc(s, sizeof(ub4)*s->dim[d]);
+ for (f=0; f<s->dim[d]; ++f) {
+ s->tu[d][f] = (tu_arr *)0;
+ s->n[d][f] = 0;
+ s->tc[d][f] = 0;
+ }
+ }
+
+ /* make dimension-specific lists of withouts */
+ for (wc=s->wc2; wc; wc=wc->next) {
+ without *w = wc->w;
+ int old = -1;
+ int i;
+ for (i=0; i<w->len; ++i) {
+ if (w->fe[i].d != old) {
+ wchain *wcx = (wchain *)my_alloc( s, sizeof(wchain));
+ wcx->w = w;
+ wcx->next = s->wc[w->fe[i].d];
+ s->wc[w->fe[i].d] = wcx;
+ old = w->fe[i].d;
+ }
+ }
+ }
+}
+
+/* parse the inputs */
+int parse( int argc, char *argv[], state *s)
+{
+ int i, j;
+ ub4 temp;
+ char *testfile = (char *)0;
+
+ /* internal check: we have MAX_FEATURES names for features */
+ if (strlen(feature_name) != MAX_FEATURES) {
+ printf("feature_name length is wrong, %d\n", strlen(feature_name));
+ return FALSE;
+ }
+
+ /* How many dimensions are there? Set ndim, allocate space for dim. */
+ for (temp=0, i=1; i<argc; ++i) {
+ if (argv[i][0] >= '0' && argv[i][0] <= '9') {
+ ++temp;
+ }
+ }
+ if (temp > MAX_DIMENSIONS) {
+ printf("jenny: maximum number of dimensions is %ld. %ld is too many.\n",
+ MAX_DIMENSIONS, temp);
+ return FALSE;
+ }
+ s->ndim = (ub2)temp;
+ s->dim = (ub2 *)my_alloc( s, sizeof(ub2)*(s->ndim));
+
+ /* Read the lengths of all the dimensions */
+ for (i=1, j=0; i<argc; ++i) {
+ if (argv[i][0] >= '0' && argv[i][0] <= '9') { /* dimension length */
+ sb1 *myarg = argv[i];
+ ub4 dummy;
+ ub4 curr = 0;
+
+ (void)parse_token(myarg, UB4MAXVAL, &curr, &temp);
+ if (parse_token(myarg, UB4MAXVAL, &curr, &dummy) != TOKEN_END) {
+ printf("jenny: something was trailing a dimension number\n");
+ return FALSE;
+ }
+ if (temp > MAX_FEATURES) {
+ printf("jenny: dimensions must be smaller than %d. %ld is too big.\n",
+ MAX_FEATURES, temp);
+ return FALSE;
+ }
+ if (temp < 2) {
+ printf("jenny: a dimension must have at least 2 features, not %d\n",
+ temp);
+ return FALSE;
+ }
+ s->dim[j++] = (ub2)temp;
+ } else if (argv[i][1] == 'h') {
+ int i;
+ for (i=0; i<(sizeof(jenny_doc)/sizeof(ub1 *)); ++i) {
+ printf(jenny_doc[i]);
+ }
+ return FALSE;
+ }
+ }
+
+ /* Read the rest of the arguments */
+ for (i=1; i<argc; ++i) if (argv[i][0] == '-') { /* dimension length */
+ switch(argv[i][1]) {
+ case '\0':
+ printf("jenny: '-' by itself isn't a proper argument.\n");
+ return FALSE;
+ case 'o': /* -o, file containing old tests */
+ testfile = &argv[i][2];
+ break;
+ case 'n': /* -n, get the n of "cover all n-tuples" */
+ if (!parse_n( s, &argv[i][2])) return FALSE;
+ break;
+ case 'w': /* -w, "without", a restriction */
+ if (!parse_w( s, &argv[i][2])) return FALSE;
+ break;
+ case 's': /* -s, "random", change the behavior */
+ if (!parse_s( s, &argv[i][2])) return FALSE;
+ break;
+ default:
+ printf("jenny: legal arguments are numbers, -n, -s, -w, -h, not -%c\n",
+ argv[i][1]);
+ return FALSE;
+ }
+ } /* for (each argument) if '-' */
+
+ if (s->n_final > s->ndim) {
+ printf("jenny: %ld-tuples are impossible with only %d dimensions\n",
+ s->n_final, s->ndim);
+ return FALSE;
+ }
+
+ preliminary(s); /* allocate structures, do preliminary analysis */
+
+ /* read in any old tests so we can build from that base */
+ if (testfile) {
+ if (!load( s, testfile)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/* print out a single test */
+void report( test *t, ub2 len)
+{
+ ub4 i;
+ for (i=0; i<len; ++i) {
+ printf(" %d%c", i+1, feature_name[t->f[i]]);
+ }
+ printf(" \n");
+}
+
+/* print out all the tests */
+void report_all( state *s)
+{
+ ub4 i;
+ for (i=0; i<s->ntests; ++i) {
+ report(s->t[i], s->ndim);
+ }
+}
+
+
+void start_builder( state *s, feature *tuple, ub1 n)
+{
+ ub1 i;
+ for (i=0; i<n; ++i) {
+ tuple[i].d = i;
+ tuple[i].f = 0;
+ }
+}
+
+int next_builder( state *s, feature *tuple, ub1 n)
+{
+ sb4 i = n;
+
+ while (i-- &&
+ tuple[i].d == s->ndim-n+i &&
+ tuple[i].f == s->dim[tuple[i].d]-1)
+ ;
+ if (i == -1)
+ return FALSE;
+ else if (tuple[i].f < s->dim[tuple[i].d]-1) {
+ ++tuple[i].f; /* increment feature */
+ for (i; i<n-1; ++i) { /* reset all less significant positions */
+ tuple[i+1].d = tuple[i].d+1;
+ tuple[i+1].f = 0;
+ }
+ }
+ else {
+ ++tuple[i].d; /* increment least significant non-maxed-out position */
+ tuple[i].f = (ub2)0; /* reset its feature */
+ for (i; i<n-1; ++i) { /* reset all less significant positions */
+ tuple[i+1].d = tuple[i].d+1;
+ tuple[i+1].f = 0;
+ }
+ }
+ return TRUE;
+}
+
+
+void build_tuples( state *s, ub2 d, ub2 f)
+{
+ feature offset[MAX_N]; /* n-1-tuples */
+ feature tuple[MAX_N]; /* n-tuples that include (d,f) */
+ sb4 i, j, n = s->n[d][f];
+ ub8 count = 0;
+ test *t;
+ tu_iter ctx;
+
+ if (s->tc[d][f] > 0 || s->n[d][f] == s->n_final) {
+ return; /* no need to generate more tuples */
+ }
+
+ n = ++s->n[d][f]; /* move up to a bigger tuple size */
+
+ /* get ready to insert tuples into the tuple list for (d,f) */
+ start_tuple(&ctx, &s->tu[d][f], n, &s->tc[d][f]);
+ for (i=0; i<n; ++i) {
+ tuple[i].d = 0;
+ tuple[i].f = 0;
+ }
+
+ /* Offset iterates through all n-1-tuples. Inject (d,f) into each. */
+ start_builder( s, offset, n-1);
+ for (;;) {
+ for (i=0; (i<n-1) && (offset[i].d < d); ++i) {
+ tuple[i].d = offset[i].d;
+ tuple[i].f = offset[i].f;
+ }
+ /* can't inject (d,f) into a tuple that already has d */
+ if ((i<n-1) && (offset[i].d == d))
+ goto make_next_tuple;
+ tuple[i].d = d;
+ tuple[i].f = f;
+ for (++i; i<n; ++i) {
+ tuple[i].d = offset[i-1].d;
+ tuple[i].f = offset[i-1].f;
+ }
+
+ for (i=0; i<n; ++i) {
+ s->tuple_tester->f[tuple[i].d] = tuple[i].f;
+ }
+ if (count_withouts(s->tuple_tester, s->wc2) ||
+ count_withouts(s->tuple_tester, s->wc3))
+ goto make_next_tuple;
+
+ /* is this tuple covered by the existing tests? */
+ for (j=0; j<s->ntests; ++j) {
+ test *t = s->t[j];
+ for (i=0; i<n; ++i) {
+ if (t->f[tuple[i].d] != tuple[i].f) {
+ break;
+ }
+ }
+ if (i == n) {
+ goto make_next_tuple;
+ }
+ }
+
+ /* add it to the list of uncovered tuples */
+ if (!insert_tuple(s, &ctx, tuple)) {
+ printf("jenny: could not insert tuple\n");
+ return;
+ }
+ ++count;
+
+ /* next tuple */
+ make_next_tuple:
+ for (i=0; i<n; ++i) {
+ s->tuple_tester->f[tuple[i].d] = (ub2)~0;
+ }
+ if (!next_builder(s, offset, n-1))
+ break;
+ }
+}
+
+/*
+ * Tweak the test, other than entries in tuple, so that all withouts are
+ * obeyed.
+ *
+ * Algorithm: loop through all the dimensions touched by any without, changing
+ * features in those dimensions so that the total number of withouts disobeyed
+ * decreases or stays the same. Succeed if all withouts are obeyed. Give up
+ * after MAX_NO_PROGRESS consecutive loops that fail to decrease the number of
+ * withouts disobeyed.
+ *
+ * Would it be better to find some disobeyed without, and change one of its
+ * dimensions at random? No. Consider
+ * jenny 2 2 2 -w1a2a -w1b3a -w1b3a -w2b3a -w2b3a
+ * and tentative testcase
+ * 1a 2a 3a
+ * Progress is impossible unless you change a dimension that is not currently
+ * in any disobeyed without.
+ */
+#define MAX_NO_PROGRESS 2
+int obey_withouts(
+state *s, /* global state */
+test *t, /* test being built */
+ub1 *mut) /* mut[i] = 1 if I am allowed to adjust dimension i */
+{
+ ub4 i;
+ without *w; /* one of the disobeyed withouts */
+ ub4 count; /* number of withouts currently hit */
+ ub2 ndim; /* size of dimord[] */
+ ub2 temp;
+
+ /* how many withouts are currently disobeyed? */
+ if (!count_withouts(t, s->wc2))
+ return TRUE;
+
+ /* fill dimord[] with all dimensions that can and should be tweaked */
+ for (ndim=0, i=0; i<s->ndim; ++i) {
+ if (mut[i] && s->wc[i]) {
+ s->dimord[ndim++] = i;
+ }
+ }
+
+ /* hillclimbing, with sidestepping, minimize number of withouts hit */
+ for (i=0; i<MAX_NO_PROGRESS; ++i) {
+ ub4 j;
+ ub2 best[MAX_FEATURES]; /* best features so far */
+ ub1 ok = TRUE;
+
+ for (j=ndim; j>0; --j) {
+ ub2 fcount = 0; /* count of filled elements of best */
+ ub2 mydim; /* the current dimension */
+ ub2 k;
+
+ /* walk the dimensions in a random order, no replacement */
+ mydim = flearand(&s->r) % j;
+ temp = s->dimord[mydim];
+ s->dimord[mydim] = s->dimord[j-1];
+ s->dimord[j-1] = temp;
+ mydim = s->dimord[j-1];
+
+ /* see how many withouts this dimension is disobeying */
+ count = count_withouts(t, s->wc[mydim]);
+
+ /* test every feature of this dimension, trying to make progress */
+ for (k=0; k<s->dim[mydim]; ++k) {
+ ub2 newcount;
+ t->f[mydim] = k;
+ newcount = count_withouts(t, s->wc[mydim]);
+ if (newcount <= count) {
+ if (newcount < count) {
+ i = 0; /* partial progress! */
+ fcount = 0;
+ count = newcount;
+ }
+ best[fcount++] = k;
+ }
+ }
+
+ /* choose one of the best features for this dimension at random */
+ if (fcount == 0) {
+ printf("jenny: internal error a\n");
+ } else if (fcount == 1) {
+ t->f[mydim] = best[0];
+ } else {
+ temp = (flearand(&s->r) % fcount);
+ t->f[mydim] = best[temp];
+ }
+
+ if (count > 0)
+ ok = FALSE;
+ }
+ if (ok) { /* no withouts disobeyed */
+ return TRUE;
+ }
+ }
+ return FALSE; /* failure, could not satisfy all the withouts */
+}
+
+ub4 count_tuples( state *s, test *t, int d, int f)
+{
+ ub4 count = 0;
+ ub1 n = s->n[d][f];
+ tu_iter ctx;
+ feature *this = start_tuple(&ctx, &s->tu[d][f], n, &s->tc[d][f]);
+ while (this) {
+ count += test_tuple(t->f, this, n);
+ this = next_tuple(&ctx);
+ }
+ return count;
+}
+
+ub4 maximize_coverage(
+state *s, /* global state */
+test *t, /* testcase being built, already obeys all restrictions */
+ub1 *mut, /* mut[i] = 1 if I can adjust dimension i */
+ub1 n) /* size of smallest tuple left to cover */
+{
+ ub1 progress;
+ ub2 ndim;
+ ub2 i;
+ ub4 total;
+
+ /* build a list of all the dimensions that we can modify */
+ for (ndim=0, i=0; i<s->ndim; ++i) {
+ if (mut[i]) {
+ s->dimord[ndim++] = i;
+ }
+ }
+
+ /* repeatedly loop through all dimensions, maximizing tuple coverage */
+ do {
+ progress = FALSE; /* assume no improvement in tuple coverage */
+ total = 1; /* one, for the one fixed tuple we are guaranteed to cover */
+
+ /* scramble the array of dimensions; */
+ for (i=ndim; i>1; --i) {
+ ub2 j = flearand(&s->r) % i;
+ ub2 temp = s->dimord[i-1];
+ s->dimord[i-1] = s->dimord[j];
+ s->dimord[j] = temp;
+ }
+
+ /* for every dimension that we can adjust */
+ for (i=0; i<ndim; ++i) {
+ ub2 best[MAX_FEATURES]; /* list of features with the best coverage */
+ ub2 count = 0; /* size of best[] */
+ ub2 d = s->dimord[i];
+ ub1 best_n = s->n[d][t->f[d]];
+ ub4 coverage = count_tuples(s, t, d, t->f[d]);
+ ub4 f;
+
+ /* for every feature in mydim, see if using it would improve coverage */
+ for (f=0; f<s->dim[d]; ++f) {
+ t->f[d] = f; /* switch to the new feature */
+ if (!count_withouts(t, s->wc[d])) { /* need to obey withouts */
+ ub4 new_coverage = count_tuples(s, t, d, f);
+ if (s->n[d][f] < best_n) {
+ best_n = s->n[d][f];
+ progress = TRUE;
+ coverage = new_coverage;
+ count = 0;
+ best[count++] = f;
+ } else if (s->n[d][f] == best_n && new_coverage >= coverage) {
+ if (new_coverage > coverage) {
+ progress = TRUE;
+ coverage = new_coverage;
+ count = 0;
+ }
+ best[count++] = f;
+ }
+ }
+ }
+
+ /*
+ * Change this dimension to the best features seen.
+ * Worst case, everyone was worse than the old value, so best[0] will
+ * be the old value. Coverage will be the same and still no withouts
+ * will be hit.
+ */
+ if (count == 0) {
+ printf("jenny: internal error b\n");
+ } else if (count == 1) {
+ t->f[d] = best[0];
+ } else {
+ t->f[d] = best[flearand(&s->r) % count];
+ }
+ if (s->n[d][t->f[d]] == n)
+ total += coverage;
+ }
+
+ } while (progress);
+ return total;
+}
+
+
+/*
+ * Generate one test that obeys all the restrictions and covers at
+ * least one tuple. Do not add it to the list of tests yet. Return FALSE
+ * if we couldn't satisfy the withouts while covering this tuple.
+ */
+#define MAX_ITERS 10
+ub4 generate_test( state *s, test *t, feature *tuple, ub1 n)
+{
+ int iter, i;
+ ub1 mut[MAX_DIMENSIONS]; /* mut[i] = 1 if I can adjust dimension i */
+ ub4 coverage = 0;
+
+ /* mut[i] = 1 if I can modify dimension i */
+ for (i=0; i<s->ndim; ++i) mut[i] = 1;
+ for (i=0; i<n; ++i) mut[tuple[i].d] = 0;
+
+ for (iter=0; iter<MAX_ITERS; ++iter) {
+ /* Produce a totally random testcase */
+ for (i=0; i<s->ndim; ++i) {
+ t->f[i] = flearand(&s->r) % (s->dim[i]);
+ }
+
+ /* Plug in the chosen new tuple */
+ for (i=0; i<n; ++i){
+ t->f[tuple[i].d] = tuple[i].f;
+ }
+
+ /* If we can get all the withouts obeyed, break, success */
+ if (!s->wc2 || obey_withouts(s, t, mut)) {
+ if (count_withouts(t, s->wc2)) {
+ printf("internal error without %d\n", s->wc2);
+ }
+ break;
+ }
+ }
+
+ /* quit if we were unable to satisfy the withouts */
+ if (iter == MAX_ITERS) {
+ goto done;
+ }
+
+ /*
+ * We now have a test that covers the new tuple and satisfies withouts.
+ * Do hillclimbing to cover as many new tuples as possible.
+ */
+ coverage = maximize_coverage(s, t, mut, n);
+
+ done:
+ return coverage;
+}
+
+#define GROUP_SIZE 5
+void cover_tuples( state *s)
+{
+ test *curr_test;
+ curr_test = (test *)my_alloc( s, sizeof(test));
+ curr_test->f = (ub2 *)my_alloc( s, sizeof(ub2)*s->ndim);
+
+ while (TRUE) {
+ ub4 i;
+ ub2 d;
+ test *best_test;
+ sb4 best_count = -1;
+ ub1 tuple_n = MAX_N;
+ ub4 tuple_count = 0;
+ ub1 covered = FALSE;
+ feature *tuple = (feature *)0;
+ ub4 tuple_f, tuple_d;
+
+ /* extend lists of tuples and choose one tuple to cover */
+ for (d=0; d<s->ndim; ++d) {
+ ub2 f;
+ for (f=0; f<s->dim[d]; ++f) {
+ build_tuples(s, d, f);
+ if (s->n[d][f] < tuple_n) {
+ tuple_n = s->n[d][f];
+ tuple_count = s->tc[d][f];
+ tuple = s->tu[d][f]->fe;
+ tuple_f = f;
+ tuple_d = d;
+ } else if (s->n[d][f] == tuple_n && s->tc[d][f] > tuple_count) {
+ tuple_count = s->tc[d][f];
+ tuple = s->tu[d][f]->fe;
+ tuple_f = f;
+ tuple_d = d;
+ }
+ }
+ }
+
+ if (tuple_count == 0) {
+ if (tuple_n == s->n_final)
+ break; /* no more tuples to cover, done! */
+ else
+ continue;
+ }
+
+
+ best_test = (test *)my_alloc( s, sizeof(test));
+ best_test->f = (ub2 *)my_alloc( s, sizeof(ub2)*s->ndim);
+
+ /* find a good test */
+ for (i=0; i<GROUP_SIZE; ++i) {
+ tu_iter ctx;
+ sb4 this_count;
+
+ /* generate a test that covers the first tuple */
+ if (!(this_count = generate_test(s, curr_test, tuple, tuple_n))) {
+ continue;
+ }
+ covered = TRUE;
+
+ /* see how many tuples are covered altogether */
+ if (this_count > best_count) {
+ test *temp = best_test;
+ best_test = curr_test;
+ curr_test = temp;
+
+ best_count = this_count;
+ }
+ }
+
+ if (!covered) {
+ wchain *wc = (wchain *)my_alloc( s, sizeof(wchain));
+ without *w = (without *)my_alloc( s, sizeof(without));
+ feature extra[MAX_N];
+
+ /* make a copy of tuple, because we'll be deleting it */
+ for (i=0; i<tuple_n; ++i) {
+ extra[i].d = tuple[i].d;
+ extra[i].f = tuple[i].f;
+ }
+
+ printf("Could not cover tuple ");
+ show_tuple(tuple, tuple_n);
+
+ /* add this tuple to the list of restrictions */
+ wc->w = w;
+ wc->next = s->wc3;
+ s->wc3 = wc;
+ w->fe = (feature *)0;
+ w->len = tuple_n;
+ w->fe = (feature *)my_alloc( s, sizeof(feature)*tuple_n);
+ for (i=0; i<tuple_n; ++i) {
+ w->fe[i].d = extra[i].d;
+ w->fe[i].f = extra[i].f;
+ }
+
+ for (d=0; d<s->ndim; ++d) {
+ ub2 f;
+ tu_iter ctx;
+ for (f=0; f<s->dim[d]; ++f) {
+ ub1 n = s->n[d][f];
+ feature *this = start_tuple(&ctx, &s->tu[d][f], n, &s->tc[d][f]);
+
+ /* remove all the tuples covered by it */
+ while (this) {
+ if (subset_tuple(extra, tuple_n, this, n)) {
+ this = delete_tuple(&ctx);
+ } else {
+ this = next_tuple(&ctx);
+ }
+ }
+ }
+ }
+ my_free((char *)best_test->f);
+ my_free((char *)best_test);
+ } else {
+ ub2 d;
+ for (d=0; d<s->ndim; ++d) {
+ tu_iter ctx;
+ ub2 f = best_test->f[d];
+ ub1 n = s->n[d][f];
+ feature *this = start_tuple(&ctx, &s->tu[d][f], n, &s->tc[d][f]);
+
+ /* remove all the tuples covered by it */
+ while (this) {
+ if (test_tuple(best_test->f, this, n)) {
+ this = delete_tuple(&ctx);
+ } else {
+ this = next_tuple(&ctx);
+ }
+ }
+ }
+
+ /* add it to the list of tests */
+ if (!add_test(s, best_test)) {
+ printf("jenny: exceeded maximum number of tests\n");
+ my_free((char *)curr_test->f);
+ my_free((char *)curr_test);
+ my_free((char *)best_test->f);
+ my_free((char *)best_test);
+ cleanup(s);
+ exit(0);
+ }
+ }
+ }
+
+ my_free((char *)curr_test->f);
+ my_free((char *)curr_test);
+}
+
+void prepare_reduce( state *s)
+{
+ feature tuple[MAX_N];
+ ub1 n = s->n_final;
+ ub4 t, d;
+
+ for (t=0; t<s->ntests; ++t) {
+ for (d=0; d<s->ndim; ++d) {
+ s->onec[t][d] = 0;
+ }
+ }
+
+ /* Iterate through all the tuples */
+ start_builder( s, tuple, n);
+
+ for (;;) {
+ sb4 i;
+ ub2 thistest;
+
+ for (i=0; i<n; ++i) {
+ s->tuple_tester->f[tuple[i].d] = tuple[i].f;
+ }
+ if (count_withouts(s->tuple_tester, s->wc2) ||
+ count_withouts(s->tuple_tester, s->wc3))
+ goto make_next_tuple;
+
+ for (i=0; i<s->ntests; ++i) {
+ ub1 j;
+ for (j=0; j<n; ++j)
+ if (s->t[i]->f[tuple[j].d] != tuple[j].f)
+ break;
+ if (j == n)
+ break; /* this test contains this tuple */
+ }
+
+ /* no tests cover this tuple */
+ if (i==s->ntests) {
+ printf("error: some tuple not covered at all\n");
+ } else {
+ thistest = i;
+ for (++i; i<s->ntests; ++i) {
+ ub1 j;
+ for (j=0; j<n; ++j)
+ if (s->t[i]->f[tuple[j].d] != tuple[j].f)
+ break;
+ if (j == n)
+ break; /* this test contains this tuple */
+ }
+ if (i == s->ntests) {
+ ub1 j;
+ for (j=0; j<n; ++j) {
+ tu_iter ctx;
+ (void)start_tuple(&ctx, &s->one[thistest][tuple[j].d], n,
+ &s->onec[thistest][tuple[j].d]);
+ (void)insert_tuple(s, &ctx, tuple);
+ }
+ }
+ }
+
+ make_next_tuple:
+ for (i=0; i<n; ++i) {
+ s->tuple_tester->f[tuple[i].d] = (ub2)~0;
+ }
+ if (!next_builder( s, tuple, n))
+ break;
+ }
+}
+
+/* find a test to try to eliminate */
+int which_test( state *s)
+{
+ ub4 t;
+ ub4 mincount = ~0;
+ ub4 mint = 0; /* test with the fewest once-covered tuples */
+ for (t=0; t<s->ntests; ++t) {
+ ub4 i, j=0;
+ for (i=0; i<s->ndim; ++i) {
+ j += s->onec[t][i];
+ }
+ if (j <= mincount) {
+ mincount = j;
+ mint = t;
+ }
+ }
+ return mint;
+}
+
+void reduce_tests( state *s)
+{
+ ub4 t;
+ prepare_reduce( s);
+ t = which_test( s);
+}
+
+/* Confirm that every tuple is covered by either a testcase or a without */
+int confirm( state *s)
+{
+ feature offset[MAX_N];
+ sb4 i, j, n = s->n_final;
+
+ /* Make a list of allowed but uncovered tuples */
+ for (i=0; i<n; ++i) {
+ offset[i].d = i;
+ offset[i].f = 0;
+ }
+
+ for (;;) {
+ for (i=0; i<n; ++i) {
+ s->tuple_tester->f[offset[i].d] = offset[i].f;
+ }
+ if (count_withouts(s->tuple_tester, s->wc2) ||
+ count_withouts(s->tuple_tester, s->wc3))
+ goto make_next_tuple;
+
+ /* is this tuple covered by the existing tests? */
+ for (j=0; j<s->ntests; ++j) {
+ test *t = s->t[j];
+ for (i=0; i<n; ++i) {
+ if (t->f[offset[i].d] != offset[i].f) {
+ break;
+ }
+ }
+ if (i == n) {
+ goto make_next_tuple;
+ }
+ }
+
+ printf("problem with %d%c\n", offset[0].d+1, feature_name[offset[0].f]);
+ return FALSE; /* found a tuple that is not covered */
+
+ make_next_tuple:
+ for (i=0; i<n; ++i) {
+ s->tuple_tester->f[offset[i].d] = (ub2)~0;
+ }
+ i=n;
+ while (i-- &&
+ offset[i].d == s->ndim-n+i &&
+ offset[i].f == s->dim[offset[i].d]-1)
+ ;
+ if (i == -1)
+ break; /* done */
+ else if (offset[i].f < s->dim[offset[i].d]-1) {
+ ++offset[i].f; /* increment feature */
+ for (i; i<n-1; ++i) { /* reset all less significant positions */
+ offset[i+1].d = offset[i].d+1;
+ offset[i+1].f = 0;
+ }
+ }
+ else {
+ ++offset[i].d; /* increment least significant non-maxed-out position */
+ offset[i].f = (ub2)0; /* reset its feature */
+ for (i; i<n-1; ++i) { /* reset all less significant positions */
+ offset[i+1].d = offset[i].d+1;
+ offset[i+1].f = 0;
+ }
+ }
+ }
+
+ return TRUE; /* all tuples are covered by a test or a without */
+}
+
+
+void driver( int argc, char *argv[])
+{
+ state s; /* internal state */
+
+ initialize(&s);
+
+ if (parse(argc, argv, &s)) { /* read the user's instructions */
+ cover_tuples(&s); /* generate testcases until all tuples are covered */
+ /* reduce_tests(&s); */ /* try to reduce the number of testcases */
+ if (confirm(&s)) /* doublecheck that all tuples really are covered */
+ report_all(&s); /* report the results */
+ else
+ printf("jenny: internal error, some tuples not covered\n");
+ }
+ cleanup(&s); /* deallocate everything */
+}
+
+int main( int argc, char *argv[])
+{
+ driver(argc, argv);
+ return 0;
+}
+
commit 6f1c63dbda40526817ffb2212367766e1cd63e99
Merge: c444729 294e113
Author: John Ralls <jralls at ceridwen.us>
Date: Mon Jul 16 12:52:01 2018 -0700
Merge Chris Lam's 'maint-stress-tests' into maint.
commit 294e113fec9ef22e752ad6922971b84f79614f2c
Author: Christopher Lam <christopher.lck at gmail.com>
Date: Mon Jul 16 07:14:44 2018 +0800
Small typo fix
Fixes typo in a51be5157c383b15a3bddf314b61293cff19c3dd
diff --git a/gnucash/gschemas/org.gnucash.gschema.xml.in b/gnucash/gschemas/org.gnucash.gschema.xml.in
index 6859da3..8d924c0 100644
--- a/gnucash/gschemas/org.gnucash.gschema.xml.in
+++ b/gnucash/gschemas/org.gnucash.gschema.xml.in
@@ -68,7 +68,7 @@
<key name='force-price-decimal' type="b">
<default>false</default>
<summary>Force prices to display as decimals even if they must be rounded.</summary>
- <description>If active, GnuCash will round prices as necessary to display them as decimals instead of displaying the exact fraction if the fractional part cannont be exactly represented as a decimal.</description>
+ <description>If active, GnuCash will round prices as necessary to display them as decimals instead of displaying the exact fraction if the fractional part cannot be exactly represented as a decimal.</description>
</key>
<key name="migrate-prefs-done" type="b">
<default>false</default>
diff --git a/gnucash/gtkbuilder/dialog-preferences.glade b/gnucash/gtkbuilder/dialog-preferences.glade
index 1514105..58dfc66 100644
--- a/gnucash/gtkbuilder/dialog-preferences.glade
+++ b/gnucash/gtkbuilder/dialog-preferences.glade
@@ -1405,7 +1405,7 @@ many months before the current month:</property>
<property name="receives_default">False</property>
<property name="has_tooltip">True</property>
<property name="tooltip_markup">Force prices to display as decimals even if they must be rounded.</property>
- <property name="tooltip_text" translatable="yes">If active, GnuCash will round prices as necessary to display them as decimals instead of displaying the exact fraction if the fractional part cannont be exactly represented as a decimal.</property>
+ <property name="tooltip_text" translatable="yes">If active, GnuCash will round prices as necessary to display them as decimals instead of displaying the exact fraction if the fractional part cannot be exactly represented as a decimal.</property>
<property name="halign">start</property>
<property name="margin_left">12</property>
<property name="use_underline">True</property>
commit 414ab99aa07c550882ffb0c65f8d415dfb1d7008
Author: Christopher Lam <christopher.lck at gmail.com>
Date: Mon Jul 16 00:18:58 2018 +0800
[test-stress-options] Set COMBINATORICS to full path to jenny
Setting COMBINATORICS to the full path name to jenny will enable
pairwise testing.
e.g. COMBINATORICS=/home/user/jenny/jenny ninja check
diff --git a/gnucash/report/standard-reports/test/test-stress-options.scm b/gnucash/report/standard-reports/test/test-stress-options.scm
index da2ffb6..4265273 100644
--- a/gnucash/report/standard-reports/test/test-stress-options.scm
+++ b/gnucash/report/standard-reports/test/test-stress-options.scm
@@ -17,6 +17,15 @@
(use-modules (sxml simple))
(use-modules (sxml xpath))
+;; NOTE
+;; ----
+;; SIMPLE stress tests by default
+;;
+;; PAIRWISE COMBINATORICS are enabled by setting environment variable COMBINATORICS
+;; to the fullpath for the compiled jenny from http://burtleburtle.net/bob/math/jenny.html
+;;
+;; e.g. COMBINATORICS=/home/user/jenny/jenny ninja check
+
(load "test-stress-optionslist.scm")
;; The above optionslist was generated programmatically. It was
;; generated by running a sentinel function in the middle of an
@@ -81,6 +90,18 @@
(tests)
(test-end "stress options"))
+(define jennypath
+ (get-environment-variable "COMBINATORICS"))
+
+(define jenny-exists?
+ ;; this is a simple test for presence of jenny - will check
+ ;; COMBINATORICS env exists, and running it produces exit-code of
+ ;; zero, and tests the first few letters of its output.
+ (and (string? jennypath)
+ (zero? (system jennypath))
+ (string=? (string-take (get-string-all (open-input-pipe jennypath)) 6)
+ "jenny:")))
+
(define (set-option! options section name value)
(let ((option (gnc:lookup-option options section name)))
(if option
@@ -175,8 +196,8 @@
;; the following is the n-tuple
2
(length report-options)))
- (cmdline (format #f "/home/chris/sources/jenny/jenny -n~a ~a"
- n-tuple jennyargs))
+ (cmdline (format #f "~a -n~a ~a"
+ jennypath n-tuple jennyargs))
(jennyout (get-string-all (open-input-pipe cmdline)))
(test-cases (string-split jennyout #\newline)))
(for-each
@@ -221,7 +242,7 @@
;; what strategy are we using here? simple stress test (ie tests as
;; many times as the maximum number of options) or combinatorial
;; tests (using jenny)
- (if (get-environment-variable "COMBINATORICS")
+ (if jenny-exists?
combinatorial-stress-test
simple-stress-test))
commit b8ce2b545ac83ff6e5d059a1c15c33b4789d024d
Author: Christopher Lam <christopher.lck at gmail.com>
Date: Tue Jul 10 23:33:40 2018 +0800
[stress-test] run tests on empty book and populated book
Refactor (tests) into call (create-test-data) which will add sample
txns in the environment.
As a result we can run tests twice - once before and once
after (create-test-data) which helps dislodge a few more report bugs.
diff --git a/gnucash/report/standard-reports/test/test-stress-options.scm b/gnucash/report/standard-reports/test/test-stress-options.scm
index 028d049..da2ffb6 100644
--- a/gnucash/report/standard-reports/test/test-stress-options.scm
+++ b/gnucash/report/standard-reports/test/test-stress-options.scm
@@ -225,7 +225,7 @@
combinatorial-stress-test
simple-stress-test))
-(define (tests)
+(define (create-test-data)
(let* ((env (create-test-env))
(account-alist (env-create-account-structure-alist env structure))
(bank (cdr (assoc "Bank" account-alist)))
@@ -280,33 +280,39 @@
(iota 12))
(let ((mid (floor (/ (+ (gnc-accounting-period-fiscal-start)
(gnc-accounting-period-fiscal-end)) 2))))
- (env-create-transaction env mid bank income 200))
+ (env-create-transaction env mid bank income 200))))
- (for-each
- (lambda (option-set)
- (let ((report-name (assq-ref option-set 'report-name))
- (report-guid (assq-ref option-set 'report-id))
- (report-options (assq-ref option-set 'options)))
- (if (member report-name
- ;; these reports seem to cause problems when running...
- '("Income Statement"
- "Tax Invoice"
- "Net Worth Linechart"
- "Tax Schedule Report/TXF Export"
- "Receipt"
- "Future Scheduled Transactions Summary"
- "Welcome to GnuCash"
- "Hello, World"
- "Budget Income Statement"
- "Multicolumn View"
- "General Journal"
- "Australian Tax Invoice"
- "Balance Sheet (eguile)"
- ;; "Budget Flow"
- "networth"
- ))
- (format #t "\nSkipping ~a...\n" report-name)
- (begin
- (format #t "\nTesting ~a...\n" report-name)
- (test report-name report-guid report-options)))))
- optionslist)))
+(define (run-tests prefix)
+ (for-each
+ (lambda (option-set)
+ (let ((report-name (assq-ref option-set 'report-name))
+ (report-guid (assq-ref option-set 'report-id))
+ (report-options (assq-ref option-set 'options)))
+ (if (member report-name
+ ;; these reports seem to cause problems when running...
+ '(
+ ;; eguile-based reports
+ "Tax Invoice"
+ "Receipt"
+ "Australian Tax Invoice"
+ "Balance Sheet (eguile)"
+
+ ;; tax-schedule - locale-dependent?
+ "Tax Schedule Report/TXF Export"
+
+ ;; unusual reports
+ "Welcome to GnuCash"
+ "Hello, World"
+ "Multicolumn View"
+ "General Journal"
+ ))
+ (format #t "\nSkipping ~a ~a...\n" report-name prefix)
+ (begin
+ (format #t "\nTesting ~a ~a...\n" report-name prefix)
+ (test report-name report-guid report-options)))))
+ optionslist))
+
+(define (tests)
+ (run-tests "with empty book")
+ (create-test-data)
+ (run-tests "on a populated book"))
commit aa4da810c1cc9b00c829ef77bc4ff8a8792c92a2
Author: Christopher Lam <christopher.lck at gmail.com>
Date: Sun Jul 8 14:59:30 2018 +0800
[test-stress-options] introduce combinatorial testing
This is enabled if the environment variable COMBINATORICS exists.
I guess it can be run via:
COMBINATORICS=bla ninja check
diff --git a/gnucash/report/standard-reports/test/test-stress-options.scm b/gnucash/report/standard-reports/test/test-stress-options.scm
index 92d6aaf..028d049 100644
--- a/gnucash/report/standard-reports/test/test-stress-options.scm
+++ b/gnucash/report/standard-reports/test/test-stress-options.scm
@@ -1,3 +1,5 @@
+(use-modules (ice-9 textual-ports))
+(use-modules (ice-9 popen))
(use-modules (gnucash utilities))
(use-modules (gnucash gnc-module))
(gnc:module-begin-syntax (gnc:module-load "gnucash/app-utils" 0))
@@ -10,6 +12,7 @@
(use-modules (gnucash report report-system))
(use-modules (gnucash report report-system test test-extras))
(use-modules (srfi srfi-64))
+(use-modules (srfi srfi-98))
(use-modules (gnucash engine test srfi64-extras))
(use-modules (sxml simple))
(use-modules (sxml xpath))
@@ -103,7 +106,7 @@
(list "Equity" (list (cons 'type ACCT-TYPE-EQUITY)))
))
-(define (test report-name uuid report-options)
+(define (simple-stress-test report-name uuid report-options)
(let ((options (gnc:make-report-options uuid)))
(test-assert (format #f "basic test ~a" report-name)
(gnc:options->render uuid options (string-append "stress-" report-name) "test"))
@@ -146,6 +149,82 @@
report-options)))
)))
+(define (combinatorial-stress-test report-name uuid report-options)
+ (let* ((options (gnc:make-report-options uuid))
+ (render #f))
+ (test-assert (format #f "basic test ~a" report-name)
+ (set! render
+ (gnc:options->render
+ uuid options (string-append "stress-" report-name) "test")))
+ (if render
+ (begin
+ (format #t "Testing n-tuple combinatorics for:\n~a" report-name)
+ (for-each
+ (lambda (option)
+ (format #t ",~a/~a"
+ (vector-ref option 0)
+ (vector-ref option 1)))
+ report-options)
+ (newline)
+ ;; generate combinatorics
+ (let* ((option-lengths (map (lambda (report-option)
+ (length (vector-ref report-option 3)))
+ report-options))
+ (jennyargs (string-join (map number->string option-lengths) " "))
+ (n-tuple (min
+ ;; the following is the n-tuple
+ 2
+ (length report-options)))
+ (cmdline (format #f "/home/chris/sources/jenny/jenny -n~a ~a"
+ n-tuple jennyargs))
+ (jennyout (get-string-all (open-input-pipe cmdline)))
+ (test-cases (string-split jennyout #\newline)))
+ (for-each
+ (lambda (case)
+ (unless (string-null? case)
+ (let* ((choices-str (string-filter char-alphabetic? case))
+ (choices-alpha (map char->integer (string->list choices-str)))
+ (choices (map (lambda (n)
+ (- n (if (> n 96) 97 39))) ; a-z -> 0-25, and A-Z -> 26-51
+ choices-alpha)))
+ (let loop ((option-idx (1- (length report-options)))
+ (option-summary '()))
+ (if (negative? option-idx)
+ (catch #t
+ (lambda ()
+ (gnc:options->render uuid options "stress-test" "test")
+ (format #t "[pass] ~a:~a \n"
+ report-name
+ (string-join option-summary ",")))
+ (lambda (k . args)
+ (format #t "[fail]... error (~s . ~s) options-list are:\n~a"
+ k args
+ (gnc:html-render-options-changed options #t))
+ (test-assert "logging test failure as above..."
+ #f)))
+ (let* ((option (list-ref report-options option-idx))
+ (section (vector-ref option 0))
+ (name (vector-ref option 1))
+ (value (list-ref (vector-ref option 3)
+ (list-ref choices option-idx))))
+ (set-option! options section name value)
+ (loop (1- option-idx)
+ (cons (format #f "~a"
+ (cond
+ ((boolean? value) (if value 't 'f))
+ (else value)))
+ option-summary))))))))
+ test-cases)))
+ (display "...aborted due to basic test failure"))))
+
+(define test
+ ;; what strategy are we using here? simple stress test (ie tests as
+ ;; many times as the maximum number of options) or combinatorial
+ ;; tests (using jenny)
+ (if (get-environment-variable "COMBINATORICS")
+ combinatorial-stress-test
+ simple-stress-test))
+
(define (tests)
(let* ((env (create-test-env))
(account-alist (env-create-account-structure-alist env structure))
@@ -223,8 +302,11 @@
"General Journal"
"Australian Tax Invoice"
"Balance Sheet (eguile)"
+ ;; "Budget Flow"
"networth"
))
- (format #t "Skipping ~a...\n" report-name)
- (test report-name report-guid report-options))))
+ (format #t "\nSkipping ~a...\n" report-name)
+ (begin
+ (format #t "\nTesting ~a...\n" report-name)
+ (test report-name report-guid report-options)))))
optionslist)))
commit dfe1f34573fcae11b8447f4f2c83d5418d9eac53
Author: Christopher Lam <christopher.lck at gmail.com>
Date: Sun Jul 1 14:12:39 2018 +0800
[stress-test] stress test options!
diff --git a/gnucash/report/standard-reports/test/CMakeLists.txt b/gnucash/report/standard-reports/test/CMakeLists.txt
index 0eed778..ed24291 100644
--- a/gnucash/report/standard-reports/test/CMakeLists.txt
+++ b/gnucash/report/standard-reports/test/CMakeLists.txt
@@ -10,6 +10,7 @@ set(scm_test_with_srfi64_SOURCES
test-charts.scm
test-transaction.scm
test-balance-sheet.scm
+ test-stress-options.scm
test-income-gst.scm
)
diff --git a/gnucash/report/standard-reports/test/test-stress-options.scm b/gnucash/report/standard-reports/test/test-stress-options.scm
new file mode 100644
index 0000000..92d6aaf
--- /dev/null
+++ b/gnucash/report/standard-reports/test/test-stress-options.scm
@@ -0,0 +1,230 @@
+(use-modules (gnucash utilities))
+(use-modules (gnucash gnc-module))
+(gnc:module-begin-syntax (gnc:module-load "gnucash/app-utils" 0))
+(use-modules (gnucash engine test test-extras))
+(use-modules (gnucash report standard-reports))
+(use-modules (gnucash report business-reports))
+(use-modules (gnucash report view-column))
+(use-modules (gnucash report stylesheets))
+(use-modules (gnucash report taxinvoice))
+(use-modules (gnucash report report-system))
+(use-modules (gnucash report report-system test test-extras))
+(use-modules (srfi srfi-64))
+(use-modules (gnucash engine test srfi64-extras))
+(use-modules (sxml simple))
+(use-modules (sxml xpath))
+
+(load "test-stress-optionslist.scm")
+;; The above optionslist was generated programmatically. It was
+;; generated by running a sentinel function in the middle of an
+;; existing report renderer. The sentinel function is defined as
+;; follows, then was cleaned up using emacs' indenting. No other
+;; processing was done. Only the multichoice and boolean options for
+;; most reports were dumped. Although this cannot provide whole
+;; coverage for option permutations, it can catch many errors while
+;; refactoring inner functions.
+
+;; (define (mydump)
+;; (with-output-to-file "test-stress-optionslist.scm"
+;; (lambda ()
+;; (display "(define optionslist\n (list \n")
+;; (gnc:report-templates-for-each
+;; (lambda (report-id template)
+;; (let* ((options-generator (gnc:report-template-options-generator template))
+;; (name (gnc:report-template-name template))
+;; (options (and (not (string=? name "General Journal"))
+;; (options-generator))))
+;; (define (disp d)
+;; (define (try proc)
+;; (catch 'wrong-type-arg
+;; (lambda () (proc d))
+;; (const #f)))
+;; (or (and (symbol? d) (string-append "'" (symbol->string d)))
+;; (and (list? d) (string-append "(list " (string-join (map disp d) " ") ")"))
+;; (and (pair? d) (format #f "(cons ~a . ~a)"
+;; (disp (car d))
+;; (disp (cdr d))))
+;; (try gnc-commodity-get-mnemonic)
+;; (try xaccAccountGetName)
+;; (try gnc-budget-get-name)
+;; (format #f "~s" d)))
+;; (format #t "(list (cons 'report-id ~s)\n (cons 'report-name ~s)\n (cons 'options\n (list\n"
+;; report-id (gnc:report-template-name template))
+;; (if options
+;; (gnc:options-for-each
+;; (lambda (option)
+;; (if (memq (gnc:option-type option) '(multichoice boolean))
+;; (format #t " (vector ~s ~s '~s '~s)\n"
+;; (gnc:option-section option)
+;; (gnc:option-name option)
+;; (gnc:option-type option)
+;; (case (gnc:option-type option)
+;; ((multichoice) (map (lambda (d) (vector-ref d 0)) (gnc:option-data option)))
+;; ((boolean) (list #t #f))
+;; ;; (else "\"\"")
+;; (else (disp (gnc:option-value option)))))))
+;; options)
+;; )
+;; (display ")))\n"))))
+;; (display "))\n"))))
+
+
+;; Explicitly set locale to make the report output predictable
+(setlocale LC_ALL "C")
+
+(define (run-test)
+ (test-runner-factory gnc:test-runner)
+ (test-begin "stress options")
+ (tests)
+ (test-end "stress options"))
+
+(define (set-option! options section name value)
+ (let ((option (gnc:lookup-option options section name)))
+ (if option
+ (gnc:option-set-value option value))))
+
+(define (mnemonic->commodity sym)
+ (gnc-commodity-table-lookup
+ (gnc-commodity-table-get-table (gnc-get-current-book))
+ (gnc-commodity-get-namespace (gnc-default-report-currency))
+ sym))
+
+(define structure
+ (list "Root" (list (cons 'type ACCT-TYPE-ASSET))
+ (list "Asset"
+ (list "Bank")
+ (list "GBP Bank" (list (cons 'commodity (mnemonic->commodity "GBP"))))
+ (list "Wallet"))
+ (list "Income" (list (cons 'type ACCT-TYPE-INCOME)))
+ (list "Income-GBP" (list (cons 'type ACCT-TYPE-INCOME)
+ (cons 'commodity (mnemonic->commodity "GBP"))))
+ (list "Expenses" (list (cons 'type ACCT-TYPE-EXPENSE)))
+ (list "Liabilities" (list (cons 'type ACCT-TYPE-LIABILITY)))
+ (list "Equity" (list (cons 'type ACCT-TYPE-EQUITY)))
+ ))
+
+(define (test report-name uuid report-options)
+ (let ((options (gnc:make-report-options uuid)))
+ (test-assert (format #f "basic test ~a" report-name)
+ (gnc:options->render uuid options (string-append "stress-" report-name) "test"))
+ (format #t "Testing SIMPLE combinations for:\n~a" report-name)
+ (for-each
+ (lambda (option)
+ (format #t ",~a/~a"
+ (vector-ref option 0)
+ (vector-ref option 1)))
+ report-options)
+ (newline)
+ (for-each
+ (lambda (idx)
+ (display report-name)
+ (for-each
+ (lambda (option)
+ (let* ((section (vector-ref option 0))
+ (name (vector-ref option 1))
+ (value (list-ref (vector-ref option 3)
+ (modulo idx (length (vector-ref option 3))))))
+ (set-option! options section name value)
+ (format #t ",~a"
+ (cond
+ ((boolean? value) (if value 't 'f))
+ (else value)))))
+ report-options)
+ (catch #t
+ (lambda ()
+ (gnc:options->render uuid options "stress-test" "test")
+ (display "[pass]\n"))
+ (lambda (k . args)
+ (format #t "[fail]... error: (~s . ~s) options-list are:\n~a"
+ k args
+ (gnc:html-render-options-changed options #t))
+ (test-assert "logging test failure as above..."
+ #f))))
+ (iota
+ (apply max
+ (map (lambda (opt) (length (vector-ref opt 3)))
+ report-options)))
+ )))
+
+(define (tests)
+ (let* ((env (create-test-env))
+ (account-alist (env-create-account-structure-alist env structure))
+ (bank (cdr (assoc "Bank" account-alist)))
+ (gbp-bank (cdr (assoc "GBP Bank" account-alist)))
+ (wallet (cdr (assoc "Wallet" account-alist)))
+ (income (cdr (assoc "Income" account-alist)))
+ (gbp-income (cdr (assoc "Income-GBP" account-alist)))
+ (expense (cdr (assoc "Expenses" account-alist)))
+ (liability (cdr (assoc "Liabilities" account-alist)))
+ (equity (cdr (assoc "Equity" account-alist))))
+ ;; populate datafile with old transactions
+ (env-transfer env 01 01 1970 bank expense 5 #:description "desc-1" #:num "trn1" #:memo "memo-3")
+ (env-transfer env 31 12 1969 income bank 10 #:description "desc-2" #:num "trn2" #:void-reason "void" #:notes "notes3")
+ (env-transfer env 31 12 1969 income bank 29 #:description "desc-3" #:num "trn3"
+ #:reconcile (cons #\c (gnc-dmy2time64 01 03 1970)))
+ (env-transfer env 01 02 1970 bank expense 15 #:description "desc-4" #:num "trn4" #:notes "notes2" #:memo "memo-1")
+ (env-transfer env 10 01 1970 liability expense 10 #:description "desc-5" #:num "trn5" #:void-reason "any")
+ (env-transfer env 10 01 1970 liability expense 11 #:description "desc-6" #:num "trn6" #:notes "notes1")
+ (env-transfer env 10 02 1970 bank liability 8 #:description "desc-7" #:num "trn7" #:notes "notes1" #:memo "memo-2"
+ #:reconcile (cons #\y (gnc-dmy2time64 01 03 1970)))
+ (let ((txn (xaccMallocTransaction (gnc-get-current-book)))
+ (split-1 (xaccMallocSplit (gnc-get-current-book)))
+ (split-2 (xaccMallocSplit (gnc-get-current-book)))
+ (split-3 (xaccMallocSplit (gnc-get-current-book))))
+ (xaccTransBeginEdit txn)
+ (xaccTransSetDescription txn "$100bank -> $80expenses + $20wallet")
+ (xaccTransSetCurrency txn (xaccAccountGetCommodity bank))
+ (xaccTransSetDate txn 14 02 1971)
+ (xaccSplitSetParent split-1 txn)
+ (xaccSplitSetParent split-2 txn)
+ (xaccSplitSetParent split-3 txn)
+ (xaccSplitSetAccount split-1 bank)
+ (xaccSplitSetAccount split-2 expense)
+ (xaccSplitSetAccount split-3 wallet)
+ (xaccSplitSetValue split-1 -100)
+ (xaccSplitSetValue split-2 80)
+ (xaccSplitSetValue split-3 20)
+ (xaccSplitSetAmount split-1 -100)
+ (xaccSplitSetAmount split-2 80)
+ (xaccSplitSetAmount split-3 20)
+ (xaccTransSetNotes txn "multisplit")
+ (xaccTransCommitEdit txn))
+ (let ((closing-txn (env-transfer env 31 12 1977 expense equity 111 #:description "Closing")))
+ (xaccTransSetIsClosingTxn closing-txn #t))
+ (env-transfer-foreign env 15 01 2000 gbp-bank bank 10 14 #:description "GBP 10 to USD 14")
+ (env-transfer-foreign env 15 02 2000 bank gbp-bank 9 6 #:description "USD 9 to GBP 6")
+ (for-each (lambda (m)
+ (env-transfer env 08 (1+ m) 1978 gbp-income gbp-bank 51 #:description "#51 income")
+ (env-transfer env 03 (1+ m) 1978 income bank 103 #:description "$103 income")
+ (env-transfer env 15 (1+ m) 1978 bank expense 22 #:description "$22 expense")
+ (env-transfer env 09 (1+ m) 1978 income bank 109 #:description "$109 income"))
+ (iota 12))
+ (let ((mid (floor (/ (+ (gnc-accounting-period-fiscal-start)
+ (gnc-accounting-period-fiscal-end)) 2))))
+ (env-create-transaction env mid bank income 200))
+
+ (for-each
+ (lambda (option-set)
+ (let ((report-name (assq-ref option-set 'report-name))
+ (report-guid (assq-ref option-set 'report-id))
+ (report-options (assq-ref option-set 'options)))
+ (if (member report-name
+ ;; these reports seem to cause problems when running...
+ '("Income Statement"
+ "Tax Invoice"
+ "Net Worth Linechart"
+ "Tax Schedule Report/TXF Export"
+ "Receipt"
+ "Future Scheduled Transactions Summary"
+ "Welcome to GnuCash"
+ "Hello, World"
+ "Budget Income Statement"
+ "Multicolumn View"
+ "General Journal"
+ "Australian Tax Invoice"
+ "Balance Sheet (eguile)"
+ "networth"
+ ))
+ (format #t "Skipping ~a...\n" report-name)
+ (test report-name report-guid report-options))))
+ optionslist)))
diff --git a/gnucash/report/standard-reports/test/test-stress-optionslist.scm b/gnucash/report/standard-reports/test/test-stress-optionslist.scm
new file mode 100644
index 0000000..b9c0220
--- /dev/null
+++ b/gnucash/report/standard-reports/test/test-stress-optionslist.scm
@@ -0,0 +1,972 @@
+(define optionslist
+ (list
+ (list (cons 'report-id "e9cf815f79db44bcb637d0295093ae3d")
+ (cons 'report-name "Assets Over Time")
+ (cons 'options
+ (list
+ (vector "Accounts" "Show Accounts until level" 'multichoice '(all 1 2 3 4 5 6))
+ (vector "Display" "Chart Type" 'multichoice '(barchart linechart))
+ (vector "Display" "Use Stacked Charts" 'boolean '(#t #f))
+ (vector "Display" "Sort Method" 'multichoice '(acct-code alphabetical amount))
+ (vector "Display" "Show table" 'boolean '(#t #f))
+ (vector "Display" "Show long account names" 'boolean '(#t #f))
+ (vector "General" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "General" "Step Size" 'multichoice '(DayDelta WeekDelta TwoWeekDelta MonthDelta QuarterDelta HalfYearDelta YearDelta))
+ )))
+ (list (cons 'report-id "e45218c6d76f11e7b5ef0800277ef320")
+ (cons 'report-name "Reconciliation Report")
+ (cons 'options
+ (list
+ (vector "Accounts" "Filter Type" 'multichoice '(none include exclude))
+ (vector "Display" "Sign Reverses" 'multichoice '(global none income-expense credit-accounts))
+ (vector "Display" "Description" 'boolean '(#t #f))
+ (vector "Display" "Amount" 'multichoice '(none single double))
+ (vector "Display" "Num" 'boolean '(#t #f))
+ (vector "Display" "Shares" 'boolean '(#t #f))
+ (vector "Display" "Use Full Account Name" 'boolean '(#t #f))
+ (vector "Display" "Use Full Other Account Name" 'boolean '(#t #f))
+ (vector "Display" "Detail Level" 'multichoice '(multi-line single))
+ (vector "Display" "Account Code" 'boolean '(#t #f))
+ (vector "Display" "Memo" 'boolean '(#t #f))
+ (vector "Display" "Totals" 'boolean '(#t #f))
+ (vector "Display" "Account Name" 'boolean '(#t #f))
+ (vector "Display" "Other Account Name" 'boolean '(#t #f))
+ (vector "Display" "Price" 'boolean '(#t #f))
+ (vector "Display" "Other Account Code" 'boolean '(#t #f))
+ (vector "Display" "Reconciled Date" 'boolean '(#t #f))
+ (vector "Display" "Subtotal Table" 'boolean '(#t #f))
+ (vector "Display" "Notes" 'boolean '(#t #f))
+ (vector "Display" "Date" 'boolean '(#t #f))
+ (vector "Sorting" "Secondary Sort Order" 'multichoice '(ascend descend))
+ (vector "Sorting" "Show Full Account Name" 'boolean '(#t #f))
+ (vector "Sorting" "Show Account Code" 'boolean '(#t #f))
+ (vector "Sorting" "Show Account Description" 'boolean '(#t #f))
+ (vector "Sorting" "Primary Subtotal for Date Key" 'multichoice '(none daily weekly monthly quarterly yearly))
+ (vector "Sorting" "Secondary Key" 'multichoice '(account-name account-code date reconciled-date reconciled-status register-order corresponding-acc-name corresponding-acc-code amount description number t-number memo notes none))
+ (vector "Sorting" "Add indenting columns" 'boolean '(#t #f))
+ (vector "Sorting" "Secondary Subtotal" 'boolean '(#t #f))
+ (vector "Sorting" "Primary Subtotal" 'boolean '(#t #f))
+ (vector "Sorting" "Secondary Subtotal for Date Key" 'multichoice '(none daily weekly monthly quarterly yearly))
+ (vector "Sorting" "Primary Key" 'multichoice '(account-name account-code date reconciled-date reconciled-status register-order corresponding-acc-name corresponding-acc-code amount description number t-number memo notes none))
+ (vector "Sorting" "Show Informal Debit/Credit Headers" 'boolean '(#t #f))
+ (vector "Sorting" "Primary Sort Order" 'multichoice '(ascend descend))
+ (vector "Sorting" "Show subtotals only (hide transactional data)" 'boolean '(#t #f))
+ (vector "Filter" "Closing transactions" 'multichoice '(exclude-closing include-both closing-only))
+ (vector "Filter" "Void Transactions" 'multichoice '(non-void-only void-only both))
+ (vector "Filter" "Use regular expressions for transaction filter" 'boolean '(#t #f))
+ (vector "Filter" "Use regular expressions for account name filter" 'boolean '(#t #f))
+ (vector "Filter" "Reconcile Status" 'multichoice '(all unreconciled cleared reconciled))
+ (vector "General" "Show original currency amount" 'boolean '(#t #f))
+ (vector "General" "Table for Exporting" 'boolean '(#t #f))
+ (vector "General" "Add options summary" 'multichoice '(no-match always never))
+ (vector "General" "Common Currency" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "583c313fcc484efc974c4c844404f454")
+ (cons 'report-name "Budget Income Statement")
+ (cons 'options
+ (list
+ (vector "Commodities" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "Commodities" "Show Exchange Rates" 'boolean '(#t #f))
+ (vector "Commodities" "Show Foreign Currencies" 'boolean '(#t #f))
+ (vector "Accounts" "Levels of Subaccounts" 'multichoice '(all 1 2 3 4 5 6))
+ (vector "Accounts" "Flatten list to depth limit" 'boolean '(#t #f))
+ (vector "Display" "Display as a two column report" 'boolean '(#t #f))
+ (vector "Display" "Show accounting-style rules" 'boolean '(#t #f))
+ (vector "Display" "Display accounts as hyperlinks" 'boolean '(#t #f))
+ (vector "Display" "Parent account balances" 'multichoice '(immediate-bal recursive-bal omit-bal))
+ (vector "Display" "Display in standard, income first, order" 'boolean '(#t #f))
+ (vector "Display" "Include accounts with zero total balances" 'boolean '(#t #f))
+ (vector "Display" "Label the revenue section" 'boolean '(#t #f))
+ (vector "Display" "Parent account subtotals" 'multichoice '(t f canonically-tabbed))
+ (vector "Display" "Include expense total" 'boolean '(#t #f))
+ (vector "Display" "Omit zero balance figures" 'boolean '(#t #f))
+ (vector "Display" "Include revenue total" 'boolean '(#t #f))
+ (vector "Display" "Label the expense section" 'boolean '(#t #f))
+ (vector "General" "Report for range of budget periods" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "4166a20981985fd2b07ff8cb3b7d384e")
+ (cons 'report-name "Customer Summary")
+ (cons 'options
+ (list
+ (vector "Display" "Sort Order" 'multichoice '(ascend descend))
+ (vector "Display" "Show Inactive Customers" 'boolean '(#t #f))
+ (vector "Display" "Show Lines with All Zeros" 'boolean '(#t #f))
+ (vector "Display" "Show Expense Column" 'boolean '(#t #f))
+ (vector "Display" "Sort Column" 'multichoice '(customername profit markup sales expense))
+ (vector "Display" "Show Company Address" 'boolean '(#t #f))
+ (vector "__reg" "reverse?" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "d8ba4a2e89e8479ca9f6eccdeb164588")
+ (cons 'report-name "Multicolumn View")
+ (cons 'options
+ (list
+ )))
+ (list (cons 'report-id "216cd0cf6931453ebcce85415aba7082")
+ (cons 'report-name "Trial Balance")
+ (cons 'options
+ (list
+ (vector "Commodities" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "Commodities" "Show Exchange Rates" 'boolean '(#t #f))
+ (vector "Commodities" "Show Foreign Currencies" 'boolean '(#t #f))
+ (vector "Accounts" "Levels of Subaccounts" 'multichoice '(all 1 2 3 4 5 6))
+ (vector "Entries" "Adjusting Entries Pattern is regular expression" 'boolean '(#t #f))
+ (vector "Entries" "Closing Entries pattern is case-sensitive" 'boolean '(#t #f))
+ (vector "Entries" "Adjusting Entries pattern is case-sensitive" 'boolean '(#t #f))
+ (vector "Entries" "Closing Entries Pattern is regular expression" 'boolean '(#t #f))
+ (vector "Display" "Display accounts as hyperlinks" 'boolean '(#t #f))
+ (vector "General" "Report variation" 'multichoice '(current pre-adj work-sheet))
+ )))
+ (list (cons 'report-id "47f45d7d6d57b68518481c1fc8d4e4ba")
+ (cons 'report-name "Future Scheduled Transactions Summary")
+ (cons 'options
+ (list
+ (vector "Commodities" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "Commodities" "Show Exchange Rates" 'boolean '(#t #f))
+ (vector "Commodities" "Show Foreign Currencies" 'boolean '(#t #f))
+ (vector "Accounts" "Levels of Subaccounts" 'multichoice '(all 1 2 3 4 5 6))
+ (vector "Accounts" "Depth limit behavior" 'multichoice '(summarize flatten truncate))
+ (vector "Display" "Show accounting-style rules" 'boolean '(#t #f))
+ (vector "Display" "Display accounts as hyperlinks" 'boolean '(#t #f))
+ (vector "Display" "Parent account balances" 'multichoice '(immediate-bal recursive-bal omit-bal))
+ (vector "Display" "Account Description" 'boolean '(#t #f))
+ (vector "Display" "Include accounts with zero total balances" 'boolean '(#t #f))
+ (vector "Display" "Parent account subtotals" 'multichoice '(t f canonically-tabbed))
+ (vector "Display" "Account Code" 'boolean '(#t #f))
+ (vector "Display" "Omit zero balance figures" 'boolean '(#t #f))
+ (vector "Display" "Account Notes" 'boolean '(#t #f))
+ (vector "Display" "Account Type" 'boolean '(#t #f))
+ (vector "Display" "Account Balance" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "c2a996c8970f43448654ca84f17dda24")
+ (cons 'report-name "Equity Statement")
+ (cons 'options
+ (list
+ (vector "Commodities" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "Commodities" "Show Exchange Rates" 'boolean '(#t #f))
+ (vector "Commodities" "Show Foreign Currencies" 'boolean '(#t #f))
+ (vector "Entries" "Closing Entries pattern is case-sensitive" 'boolean '(#t #f))
+ (vector "Entries" "Closing Entries Pattern is regular expression" 'boolean '(#t #f))
+ (vector "Display" "Show accounting-style rules" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "3dbbc2584da64e7a8674355bc3fbfe3d")
+ (cons 'report-name "Australian Tax Invoice")
+ (cons 'options
+ (list
+ (vector "Display" "table-border-collapse" 'boolean '(#t #f))
+ (vector "Elements" "Show net price" 'boolean '(#t #f))
+ (vector "Elements" "column: Units" 'boolean '(#t #f))
+ (vector "Elements" "Show Job number" 'boolean '(#t #f))
+ (vector "Elements" "row: Address" 'boolean '(#t #f))
+ (vector "Elements" "column: Date" 'boolean '(#t #f))
+ (vector "Elements" "column: Tax Rate" 'boolean '(#t #f))
+ (vector "Elements" "Invoice number next to title" 'boolean '(#t #f))
+ (vector "Elements" "row: Company Name" 'boolean '(#t #f))
+ (vector "Elements" "row: Invoice Number" 'boolean '(#t #f))
+ (vector "Elements" "Show Job name" 'boolean '(#t #f))
+ (vector "Elements" "row: Contact" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "2e3751edeb7544e8a20fd19e9d08bb65")
+ (cons 'report-name "Balance Sheet (eguile)")
+ (cons 'options
+ (list
+ (vector "Commodities" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "Commodities" "Show Foreign Currencies" 'boolean '(#t #f))
+ (vector "Accounts" "Display accounts as hyperlinks" 'boolean '(#t #f))
+ (vector "Accounts" "Levels of Subaccounts" 'multichoice '(all 1 2 3 4 5 6))
+ (vector "Accounts" "Flatten list to depth limit" 'boolean '(#t #f))
+ (vector "Accounts" "Exclude accounts with zero total balances" 'boolean '(#t #f))
+ (vector "Display" "Negative amount format" 'multichoice '(negsign negbrackets))
+ (vector "Display" "1- or 2-column report" 'multichoice '(autocols onecol twocols))
+ )))
+ (list (cons 'report-id "e9418ff64f2c11e5b61d1c7508d793ed")
+ (cons 'report-name "Securities")
+ (cons 'options
+ (list
+ (vector "Display" "Show Totals" 'boolean '(#t #f))
+ (vector "Display" "Show long names" 'boolean '(#t #f))
+ (vector "Display" "Show Percents" 'boolean '(#t #f))
+ (vector "Display" "Sort Method" 'multichoice '(acct-code alphabetical amount))
+ (vector "General" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ )))
+ (list (cons 'report-id "e1bd09b8a1dd49dd85760db9d82b045c")
+ (cons 'report-name "Income Accounts")
+ (cons 'options
+ (list
+ (vector "Accounts" "Show Accounts until level" 'multichoice '(all 1 2 3 4 5 6))
+ (vector "Display" "Show Totals" 'boolean '(#t #f))
+ (vector "Display" "Show long names" 'boolean '(#t #f))
+ (vector "Display" "Show Percents" 'boolean '(#t #f))
+ (vector "Display" "Sort Method" 'multichoice '(acct-code alphabetical amount))
+ (vector "General" "Show Average" 'multichoice '(None YearDelta MonthDelta WeekDelta))
+ (vector "General" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ )))
+ (list (cons 'report-id "2e22929e5c5b4b769f615a815ef0c20f")
+ (cons 'report-name "General Ledger")
+ (cons 'options
+ (list
+ (vector "Accounts" "Filter Type" 'multichoice '(none include exclude))
+ (vector "Display" "Sign Reverses" 'multichoice '(global none income-expense credit-accounts))
+ (vector "Display" "Description" 'boolean '(#t #f))
+ (vector "Display" "Amount" 'multichoice '(none single double))
+ (vector "Display" "Num" 'boolean '(#t #f))
+ (vector "Display" "Running Balance" 'boolean '(#t #f))
+ (vector "Display" "Shares" 'boolean '(#t #f))
+ (vector "Display" "Use Full Account Name" 'boolean '(#t #f))
+ (vector "Display" "Use Full Other Account Name" 'boolean '(#t #f))
+ (vector "Display" "Detail Level" 'multichoice '(multi-line single))
+ (vector "Display" "Account Code" 'boolean '(#t #f))
+ (vector "Display" "Memo" 'boolean '(#t #f))
+ (vector "Display" "Totals" 'boolean '(#t #f))
+ (vector "Display" "Account Name" 'boolean '(#t #f))
+ (vector "Display" "Other Account Name" 'boolean '(#t #f))
+ (vector "Display" "Price" 'boolean '(#t #f))
+ (vector "Display" "Other Account Code" 'boolean '(#t #f))
+ (vector "Display" "Reconciled Date" 'boolean '(#t #f))
+ (vector "Display" "Subtotal Table" 'boolean '(#t #f))
+ (vector "Display" "Notes" 'boolean '(#t #f))
+ (vector "Display" "Date" 'boolean '(#t #f))
+ (vector "Sorting" "Secondary Sort Order" 'multichoice '(ascend descend))
+ (vector "Sorting" "Show Full Account Name" 'boolean '(#t #f))
+ (vector "Sorting" "Show Account Code" 'boolean '(#t #f))
+ (vector "Sorting" "Show Account Description" 'boolean '(#t #f))
+ (vector "Sorting" "Primary Subtotal for Date Key" 'multichoice '(none daily weekly monthly quarterly yearly))
+ (vector "Sorting" "Secondary Key" 'multichoice '(account-name account-code date reconciled-date reconciled-status register-order corresponding-acc-name corresponding-acc-code amount description number t-number memo notes none))
+ (vector "Sorting" "Add indenting columns" 'boolean '(#t #f))
+ (vector "Sorting" "Secondary Subtotal" 'boolean '(#t #f))
+ (vector "Sorting" "Primary Subtotal" 'boolean '(#t #f))
+ (vector "Sorting" "Secondary Subtotal for Date Key" 'multichoice '(none daily weekly monthly quarterly yearly))
+ (vector "Sorting" "Primary Key" 'multichoice '(account-name account-code date reconciled-date reconciled-status register-order corresponding-acc-name corresponding-acc-code amount description number t-number memo notes none))
+ (vector "Sorting" "Show Informal Debit/Credit Headers" 'boolean '(#t #f))
+ (vector "Sorting" "Primary Sort Order" 'multichoice '(ascend descend))
+ (vector "Sorting" "Show subtotals only (hide transactional data)" 'boolean '(#t #f))
+ (vector "Filter" "Closing transactions" 'multichoice '(exclude-closing include-both closing-only))
+ (vector "Filter" "Void Transactions" 'multichoice '(non-void-only void-only both))
+ (vector "Filter" "Use regular expressions for transaction filter" 'boolean '(#t #f))
+ (vector "Filter" "Use regular expressions for account name filter" 'boolean '(#t #f))
+ (vector "Filter" "Reconcile Status" 'multichoice '(all unreconciled cleared reconciled))
+ (vector "General" "Stylesheet" 'multichoice '(Default Easy Footer #{Head or Tail}# Technicolor))
+ (vector "General" "Show original currency amount" 'boolean '(#t #f))
+ (vector "General" "Table for Exporting" 'boolean '(#t #f))
+ (vector "General" "Add options summary" 'multichoice '(no-match always never))
+ (vector "General" "Common Currency" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "9cf76bed17f14401b8e3e22d0079cb98")
+ (cons 'report-name "Receivable Aging")
+ (cons 'options
+ (list
+ (vector "Display" "Address Name" 'boolean '(#t #f))
+ (vector "Display" "Address 1" 'boolean '(#t #f))
+ (vector "Display" "Active" 'boolean '(#t #f))
+ (vector "Display" "Address 2" 'boolean '(#t #f))
+ (vector "Display" "Address 3" 'boolean '(#t #f))
+ (vector "Display" "Address Source" 'multichoice '(billing shipping))
+ (vector "Display" "Address Email" 'boolean '(#t #f))
+ (vector "Display" "Address Phone" 'boolean '(#t #f))
+ (vector "Display" "Address 4" 'boolean '(#t #f))
+ (vector "Display" "Address Fax" 'boolean '(#t #f))
+ (vector "General" "Sort By" 'multichoice '(name total oldest-bracket))
+ (vector "General" "Show zero balance items" 'boolean '(#t #f))
+ (vector "General" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "General" "Sort Order" 'multichoice '(increasing decreasing))
+ (vector "General" "Due or Post Date" 'multichoice '(duedate postdate))
+ (vector "General" "Show Multi-currency Totals" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "3ce293441e894423a2425d7a22dd1ac6")
+ (cons 'report-name "Fancy Invoice")
+ (cons 'options
+ (list
+ (vector "Display" "Payable to" 'boolean '(#t #f))
+ (vector "Display" "Individual Taxes" 'boolean '(#t #f))
+ (vector "Display" "Billing Terms" 'boolean '(#t #f))
+ (vector "Display" "References" 'boolean '(#t #f))
+ (vector "Display" "Totals" 'boolean '(#t #f))
+ (vector "Display" "Invoice Notes" 'boolean '(#t #f))
+ (vector "Display" "Billing ID" 'boolean '(#t #f))
+ (vector "Display" "Payments" 'boolean '(#t #f))
+ (vector "Display" "Company contact" 'boolean '(#t #f))
+ (vector "Display Columns" "Tax Amount" 'boolean '(#t #f))
+ (vector "Display Columns" "Description" 'boolean '(#t #f))
+ (vector "Display Columns" "Discount" 'boolean '(#t #f))
+ (vector "Display Columns" "Total" 'boolean '(#t #f))
+ (vector "Display Columns" "Action" 'boolean '(#t #f))
+ (vector "Display Columns" "Taxable" 'boolean '(#t #f))
+ (vector "Display Columns" "Quantity" 'boolean '(#t #f))
+ (vector "Display Columns" "Price" 'boolean '(#t #f))
+ (vector "Display Columns" "Date" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "08ae9c2e884b4f9787144f47eacd7f44")
+ (cons 'report-name "Employee Report")
+ (cons 'options
+ (list
+ (vector "Display Columns" "Tax" 'boolean '(#t #f))
+ (vector "Display Columns" "Description" 'boolean '(#t #f))
+ (vector "Display Columns" "Amount" 'boolean '(#t #f))
+ (vector "Display Columns" "Debits" 'boolean '(#t #f))
+ (vector "Display Columns" "Type" 'boolean '(#t #f))
+ (vector "Display Columns" "Due Date" 'boolean '(#t #f))
+ (vector "Display Columns" "Sale" 'boolean '(#t #f))
+ (vector "Display Columns" "Reference" 'boolean '(#t #f))
+ (vector "Display Columns" "Credits" 'boolean '(#t #f))
+ (vector "Display Columns" "Date" 'boolean '(#t #f))
+ (vector "General" "Due or Post Date" 'multichoice '(duedate postdate))
+ (vector "__reg" "reverse?" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "8758ba23984c40dea5527f5f0ca2779e")
+ (cons 'report-name "Profit & Loss")
+ (cons 'options
+ (list
+ (vector "Commodities" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "Commodities" "Show Exchange Rates" 'boolean '(#t #f))
+ (vector "Commodities" "Show Foreign Currencies" 'boolean '(#t #f))
+ (vector "Accounts" "Levels of Subaccounts" 'multichoice '(all 1 2 3 4 5 6))
+ (vector "Accounts" "Flatten list to depth limit" 'boolean '(#t #f))
+ (vector "Entries" "Closing Entries pattern is case-sensitive" 'boolean '(#t #f))
+ (vector "Entries" "Closing Entries Pattern is regular expression" 'boolean '(#t #f))
+ (vector "Display" "Display as a two column report" 'boolean '(#t #f))
+ (vector "Display" "Show accounting-style rules" 'boolean '(#t #f))
+ (vector "Display" "Display accounts as hyperlinks" 'boolean '(#t #f))
+ (vector "Display" "Parent account balances" 'multichoice '(immediate-bal recursive-bal omit-bal))
+ (vector "Display" "Display in standard, income first, order" 'boolean '(#t #f))
+ (vector "Display" "Include accounts with zero total balances" 'boolean '(#t #f))
+ (vector "Display" "Label the revenue section" 'boolean '(#t #f))
+ (vector "Display" "Parent account subtotals" 'multichoice '(t f canonically-tabbed))
+ (vector "Display" "Include expense total" 'boolean '(#t #f))
+ (vector "Display" "Include trading accounts total" 'boolean '(#t #f))
+ (vector "Display" "Omit zero balance figures" 'boolean '(#t #f))
+ (vector "Display" "Include revenue total" 'boolean '(#t #f))
+ (vector "Display" "Label the expense section" 'boolean '(#t #f))
+ (vector "Display" "Label the trading accounts section" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "c4173ac99b2b448289bf4d11c731af13")
+ (cons 'report-name "Balance Sheet")
+ (cons 'options
+ (list
+ (vector "Commodities" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "Commodities" "Show Exchange Rates" 'boolean '(#t #f))
+ (vector "Commodities" "Show Foreign Currencies" 'boolean '(#t #f))
+ (vector "Accounts" "Levels of Subaccounts" 'multichoice '(all 1 2 3 4 5 6))
+ (vector "Accounts" "Flatten list to depth limit" 'boolean '(#t #f))
+ (vector "Display" "Show accounting-style rules" 'boolean '(#t #f))
+ (vector "Display" "Display accounts as hyperlinks" 'boolean '(#t #f))
+ (vector "Display" "Parent account balances" 'multichoice '(immediate-bal recursive-bal omit-bal))
+ (vector "Display" "Include assets total" 'boolean '(#t #f))
+ (vector "Display" "Include accounts with zero total balances" 'boolean '(#t #f))
+ (vector "Display" "Parent account subtotals" 'multichoice '(t f canonically-tabbed))
+ (vector "Display" "Include liabilities total" 'boolean '(#t #f))
+ (vector "Display" "Label the liabilities section" 'boolean '(#t #f))
+ (vector "Display" "Omit zero balance figures" 'boolean '(#t #f))
+ (vector "Display" "Label the assets section" 'boolean '(#t #f))
+ (vector "Display" "Include equity total" 'boolean '(#t #f))
+ (vector "Display" "Label the equity section" 'boolean '(#t #f))
+ (vector "General" "Single column Balance Sheet" 'boolean '(#t #f))
+ (vector "General" "Use standard US layout" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "c146317be32e4948a561ec7fc89d15c1")
+ (cons 'report-name "Customer Report")
+ (cons 'options
+ (list
+ (vector "Display Columns" "Tax" 'boolean '(#t #f))
+ (vector "Display Columns" "Description" 'boolean '(#t #f))
+ (vector "Display Columns" "Amount" 'boolean '(#t #f))
+ (vector "Display Columns" "Debits" 'boolean '(#t #f))
+ (vector "Display Columns" "Type" 'boolean '(#t #f))
+ (vector "Display Columns" "Due Date" 'boolean '(#t #f))
+ (vector "Display Columns" "Sale" 'boolean '(#t #f))
+ (vector "Display Columns" "Reference" 'boolean '(#t #f))
+ (vector "Display Columns" "Credits" 'boolean '(#t #f))
+ (vector "Display Columns" "Date" 'boolean '(#t #f))
+ (vector "General" "Due or Post Date" 'multichoice '(duedate postdate))
+ (vector "__reg" "reverse?" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "2fe3b9833af044abb929a88d5a59620f")
+ (cons 'report-name "Transaction Report")
+ (cons 'options
+ (list
+ (vector "Accounts" "Filter Type" 'multichoice '(none include exclude))
+ (vector "Display" "Sign Reverses" 'multichoice '(global none income-expense credit-accounts))
+ (vector "Display" "Description" 'boolean '(#t #f))
+ (vector "Display" "Amount" 'multichoice '(none single double))
+ (vector "Display" "Num" 'boolean '(#t #f))
+ (vector "Display" "Running Balance" 'boolean '(#t #f))
+ (vector "Display" "Shares" 'boolean '(#t #f))
+ (vector "Display" "Use Full Account Name" 'boolean '(#t #f))
+ (vector "Display" "Use Full Other Account Name" 'boolean '(#t #f))
+ (vector "Display" "Detail Level" 'multichoice '(multi-line single))
+ (vector "Display" "Account Code" 'boolean '(#t #f))
+ (vector "Display" "Memo" 'boolean '(#t #f))
+ (vector "Display" "Totals" 'boolean '(#t #f))
+ (vector "Display" "Account Name" 'boolean '(#t #f))
+ (vector "Display" "Other Account Name" 'boolean '(#t #f))
+ (vector "Display" "Price" 'boolean '(#t #f))
+ (vector "Display" "Other Account Code" 'boolean '(#t #f))
+ (vector "Display" "Reconciled Date" 'boolean '(#t #f))
+ (vector "Display" "Subtotal Table" 'boolean '(#t #f))
+ (vector "Display" "Notes" 'boolean '(#t #f))
+ (vector "Display" "Date" 'boolean '(#t #f))
+ (vector "Sorting" "Secondary Sort Order" 'multichoice '(ascend descend))
+ (vector "Sorting" "Show Full Account Name" 'boolean '(#t #f))
+ (vector "Sorting" "Show Account Code" 'boolean '(#t #f))
+ (vector "Sorting" "Show Account Description" 'boolean '(#t #f))
+ (vector "Sorting" "Primary Subtotal for Date Key" 'multichoice '(none daily weekly monthly quarterly yearly))
+ (vector "Sorting" "Secondary Key" 'multichoice '(account-name account-code date reconciled-date reconciled-status register-order corresponding-acc-name corresponding-acc-code amount description number t-number memo notes none))
+ (vector "Sorting" "Add indenting columns" 'boolean '(#t #f))
+ (vector "Sorting" "Secondary Subtotal" 'boolean '(#t #f))
+ (vector "Sorting" "Primary Subtotal" 'boolean '(#t #f))
+ (vector "Sorting" "Secondary Subtotal for Date Key" 'multichoice '(none daily weekly monthly quarterly yearly))
+ (vector "Sorting" "Primary Key" 'multichoice '(account-name account-code date reconciled-date reconciled-status register-order corresponding-acc-name corresponding-acc-code amount description number t-number memo notes none))
+ (vector "Sorting" "Show Informal Debit/Credit Headers" 'boolean '(#t #f))
+ (vector "Sorting" "Primary Sort Order" 'multichoice '(ascend descend))
+ (vector "Sorting" "Show subtotals only (hide transactional data)" 'boolean '(#t #f))
+ (vector "Filter" "Closing transactions" 'multichoice '(exclude-closing include-both closing-only))
+ (vector "Filter" "Void Transactions" 'multichoice '(non-void-only void-only both))
+ (vector "Filter" "Use regular expressions for transaction filter" 'boolean '(#t #f))
+ (vector "Filter" "Use regular expressions for account name filter" 'boolean '(#t #f))
+ (vector "Filter" "Reconcile Status" 'multichoice '(all unreconciled cleared reconciled))
+ (vector "General" "Show original currency amount" 'boolean '(#t #f))
+ (vector "General" "Table for Exporting" 'boolean '(#t #f))
+ (vector "General" "Add options summary" 'multichoice '(no-match always never))
+ (vector "General" "Common Currency" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "e57770f2dbca46619d6dac4ac5469b50")
+ (cons 'report-name "Payable Aging")
+ (cons 'options
+ (list
+ (vector "Display" "Address Name" 'boolean '(#t #f))
+ (vector "Display" "Address 1" 'boolean '(#t #f))
+ (vector "Display" "Active" 'boolean '(#t #f))
+ (vector "Display" "Address 2" 'boolean '(#t #f))
+ (vector "Display" "Address 3" 'boolean '(#t #f))
+ (vector "Display" "Address Email" 'boolean '(#t #f))
+ (vector "Display" "Address Phone" 'boolean '(#t #f))
+ (vector "Display" "Address 4" 'boolean '(#t #f))
+ (vector "Display" "Address Fax" 'boolean '(#t #f))
+ (vector "General" "Sort By" 'multichoice '(name total oldest-bracket))
+ (vector "General" "Show zero balance items" 'boolean '(#t #f))
+ (vector "General" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "General" "Sort Order" 'multichoice '(increasing decreasing))
+ (vector "General" "Due or Post Date" 'multichoice '(duedate postdate))
+ (vector "General" "Show Multi-currency Totals" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "5426e4d987f6444387fe70880e5b28a0")
+ (cons 'report-name "Cash Flow Barchart")
+ (cons 'options
+ (list
+ (vector "Accounts" "Include Trading Accounts in report" 'boolean '(#t #f))
+ (vector "Display" "Show Table" 'boolean '(#t #f))
+ (vector "Display" "Show Money In" 'boolean '(#t #f))
+ (vector "Display" "Show Money Out" 'boolean '(#t #f))
+ (vector "Display" "Show Net Flow" 'boolean '(#t #f))
+ (vector "General" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "General" "Step Size" 'multichoice '(DayDelta WeekDelta TwoWeekDelta MonthDelta QuarterDelta HalfYearDelta YearDelta))
+ )))
+ (list (cons 'report-id "0769e242be474010b4acf264a5512e6e")
+ (cons 'report-name "Tax Invoice")
+ (cons 'options
+ (list
+ (vector "Display" "table-border-collapse" 'boolean '(#t #f))
+ (vector "Elements" "Show net price" 'boolean '(#t #f))
+ (vector "Elements" "column: Units" 'boolean '(#t #f))
+ (vector "Elements" "Show Job number" 'boolean '(#t #f))
+ (vector "Elements" "row: Address" 'boolean '(#t #f))
+ (vector "Elements" "column: Date" 'boolean '(#t #f))
+ (vector "Elements" "column: Tax Rate" 'boolean '(#t #f))
+ (vector "Elements" "Invoice number next to title" 'boolean '(#t #f))
+ (vector "Elements" "row: Company Name" 'boolean '(#t #f))
+ (vector "Elements" "row: Invoice Number" 'boolean '(#t #f))
+ (vector "Elements" "Show Job name" 'boolean '(#t #f))
+ (vector "Elements" "row: Contact" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "dde49fed4ca940959ae7d01b72742530")
+ (cons 'report-name "Expenses vs. Day of Week")
+ (cons 'options
+ (list
+ (vector "Accounts" "Include Sub-Accounts" 'boolean '(#t #f))
+ (vector "Accounts" "Show Accounts until level" 'multichoice '(all 1 2 3 4 5 6))
+ (vector "Display" "Show Totals" 'boolean '(#t #f))
+ (vector "General" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ )))
+ (list (cons 'report-id "d5adcc61c62e4b8684dd8907448d7900")
+ (cons 'report-name "Average Balance")
+ (cons 'options
+ (list
+ (vector "Accounts" "Include Sub-Accounts" 'boolean '(#t #f))
+ (vector "Accounts" "Exclude transactions between selected accounts" 'boolean '(#t #f))
+ (vector "Display" "Show table" 'boolean '(#t #f))
+ (vector "Display" "Show plot" 'boolean '(#t #f))
+ (vector "General" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "General" "Step Size" 'multichoice '(DayDelta WeekDelta TwoWeekDelta MonthDelta QuarterDelta HalfYearDelta YearDelta))
+ )))
+ (list (cons 'report-id "25455562bd234dd0b048ecc5a8af9e43")
+ (cons 'report-name "General Journal")
+ (cons 'options
+ (list
+ )))
+ (list (cons 'report-id "67112f318bef4fc496bdc27d106bbda4")
+ (cons 'report-name "Easy Invoice")
+ (cons 'options
+ (list
+ (vector "Display" "My Company" 'boolean '(#t #f))
+ (vector "Display" "Subtotal" 'boolean '(#t #f))
+ (vector "Display" "Individual Taxes" 'boolean '(#t #f))
+ (vector "Display" "Billing Terms" 'boolean '(#t #f))
+ (vector "Display" "Due Date" 'boolean '(#t #f))
+ (vector "Display" "References" 'boolean '(#t #f))
+ (vector "Display" "Totals" 'boolean '(#t #f))
+ (vector "Display" "My Company ID" 'boolean '(#t #f))
+ (vector "Display" "Invoice Notes" 'boolean '(#t #f))
+ (vector "Display" "Billing ID" 'boolean '(#t #f))
+ (vector "Display" "Payments" 'boolean '(#t #f))
+ (vector "Display Columns" "Tax Amount" 'boolean '(#t #f))
+ (vector "Display Columns" "Description" 'boolean '(#t #f))
+ (vector "Display Columns" "Discount" 'boolean '(#t #f))
+ (vector "Display Columns" "Total" 'boolean '(#t #f))
+ (vector "Display Columns" "Charge Type" 'boolean '(#t #f))
+ (vector "Display Columns" "Taxable" 'boolean '(#t #f))
+ (vector "Display Columns" "Quantity" 'boolean '(#t #f))
+ (vector "Display Columns" "Price" 'boolean '(#t #f))
+ (vector "Display Columns" "Date" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "3298541c236b494998b236dfad6ad752")
+ (cons 'report-name "Account Summary")
+ (cons 'options
+ (list
+ (vector "Commodities" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "Commodities" "Show Exchange Rates" 'boolean '(#t #f))
+ (vector "Commodities" "Show Foreign Currencies" 'boolean '(#t #f))
+ (vector "Accounts" "Levels of Subaccounts" 'multichoice '(all 1 2 3 4 5 6))
+ (vector "Accounts" "Depth limit behavior" 'multichoice '(summarize flatten truncate))
+ (vector "Display" "Show accounting-style rules" 'boolean '(#t #f))
+ (vector "Display" "Display accounts as hyperlinks" 'boolean '(#t #f))
+ (vector "Display" "Parent account balances" 'multichoice '(immediate-bal recursive-bal omit-bal))
+ (vector "Display" "Account Description" 'boolean '(#t #f))
+ (vector "Display" "Include accounts with zero total balances" 'boolean '(#t #f))
+ (vector "Display" "Parent account subtotals" 'multichoice '(t f canonically-tabbed))
+ (vector "Display" "Account Code" 'boolean '(#t #f))
+ (vector "Display" "Omit zero balance figures" 'boolean '(#t #f))
+ (vector "Display" "Account Notes" 'boolean '(#t #f))
+ (vector "Display" "Account Type" 'boolean '(#t #f))
+ (vector "Display" "Account Balance" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "80769921e87943adade887b9835a7685")
+ (cons 'report-name "Income/Expense Chart")
+ (cons 'options
+ (list
+ (vector "Display" "Show Income/Expense" 'boolean '(#t #f))
+ (vector "Display" "Show table" 'boolean '(#t #f))
+ (vector "Display" "Show Net Profit" 'boolean '(#t #f))
+ (vector "General" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "General" "Step Size" 'multichoice '(DayDelta WeekDelta TwoWeekDelta MonthDelta QuarterDelta HalfYearDelta YearDelta))
+ )))
+ (list (cons 'report-id "f8921f4e5c284d7caca81e239f468a68")
+ (cons 'report-name "Tax Schedule Report/TXF Export")
+ (cons 'options
+ (list
+ (vector "Display" "Do not print transaction detail" 'boolean '(#t #f))
+ (vector "Display" "Do not print Action:Memo data" 'boolean '(#t #f))
+ (vector "Display" "Currency conversion date" 'multichoice '(conv-to-tran-date conv-to-report-date))
+ (vector "Display" "Print TXF export parameters" 'boolean '(#t #f))
+ (vector "Display" "Print all Transfer To/From Accounts" 'boolean '(#t #f))
+ (vector "Display" "Suppress $0.00 values" 'boolean '(#t #f))
+ (vector "Display" "Do not use special date processing" 'boolean '(#t #f))
+ (vector "Display" "Do not print full account names" 'boolean '(#t #f))
+ (vector "General" "Alternate Period" 'multichoice '(from-to #{1st-est}# #{2nd-est}# #{3rd-est}# #{4th-est}# last-year #{1st-last}# #{2nd-last}# #{3rd-last}# #{4th-last}#))
+ )))
+ (list (cons 'report-id "810ed4b25ef0486ea43bbd3dddb32b11")
+ (cons 'report-name "Budget Report")
+ (cons 'options
+ (list
+ (vector "Accounts" "Always show sub-accounts" 'boolean '(#t #f))
+ (vector "Accounts" "Flatten list to depth limit" 'boolean '(#t #f))
+ (vector "Accounts" "Account Display Depth" 'multichoice '(all 1 2 3 4 5 6))
+ (vector "Display" "Show Actual" 'boolean '(#t #f))
+ (vector "Display" "Show Budget" 'boolean '(#t #f))
+ (vector "Display" "Include accounts with zero total balances and budget values" 'boolean '(#t #f))
+ (vector "Display" "Roll up budget amounts to parent" 'boolean '(#t #f))
+ (vector "Display" "Show Column with Totals" 'boolean '(#t #f))
+ (vector "Display" "Show Difference" 'boolean '(#t #f))
+ (vector "General" "Report for range of budget periods" 'boolean '(#t #f))
+ (vector "General" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "General" "Range end" 'multichoice '(first previous current next last manual))
+ (vector "General" "Include collapsed periods before selected." 'boolean '(#t #f))
+ (vector "General" "Range start" 'multichoice '(first previous current next last manual))
+ (vector "General" "Include collapsed periods after selected." 'boolean '(#t #f))
+ (vector "General" "Show Full Account Names" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "cbba1696c8c24744848062c7f1cf4a72")
+ (cons 'report-name "Net Worth Barchart")
+ (cons 'options
+ (list
+ (vector "Display" "Show Asset & Liability" 'boolean '(#t #f))
+ (vector "Display" "Show table" 'boolean '(#t #f))
+ (vector "Display" "Show Net Worth" 'boolean '(#t #f))
+ (vector "General" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "General" "Step Size" 'multichoice '(DayDelta WeekDelta TwoWeekDelta MonthDelta QuarterDelta HalfYearDelta YearDelta))
+ )))
+ (list (cons 'report-id "4a6b82e8678c4f3d9e85d9f09634ca89")
+ (cons 'report-name "Investment Portfolio")
+ (cons 'options
+ (list
+ (vector "Accounts" "Include accounts with no shares" 'boolean '(#t #f))
+ (vector "General" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ )))
+ (list (cons 'report-id "44f81bee049b4b3ea908f8dac9a9474e")
+ (cons 'report-name "Income Over Time")
+ (cons 'options
+ (list
+ (vector "Accounts" "Show Accounts until level" 'multichoice '(all 1 2 3 4 5 6))
+ (vector "Display" "Chart Type" 'multichoice '(barchart linechart))
+ (vector "Display" "Use Stacked Charts" 'boolean '(#t #f))
+ (vector "Display" "Sort Method" 'multichoice '(acct-code alphabetical amount))
+ (vector "Display" "Show table" 'boolean '(#t #f))
+ (vector "Display" "Show long account names" 'boolean '(#t #f))
+ (vector "General" "Show Average" 'multichoice '(None MonthDelta WeekDelta DayDelta))
+ (vector "General" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "General" "Step Size" 'multichoice '(DayDelta WeekDelta TwoWeekDelta MonthDelta QuarterDelta HalfYearDelta YearDelta))
+ )))
+ (list (cons 'report-id "5bf27f249a0d11e7abc4cec278b6b50a")
+ (cons 'report-name "Income and GST Statement")
+ (cons 'options
+ (list
+ (vector "Display" "Description" 'boolean '(#t #f))
+ (vector "Display" "Num" 'boolean '(#t #f))
+ (vector "Display" "Tax payable" 'boolean '(#t #f))
+ (vector "Display" "Individual income columns" 'boolean '(#t #f))
+ (vector "Display" "Use Full Account Name" 'boolean '(#t #f))
+ (vector "Display" "Net Income" 'boolean '(#t #f))
+ (vector "Display" "Use Full Other Account Name" 'boolean '(#t #f))
+ (vector "Display" "Individual expense columns" 'boolean '(#t #f))
+ (vector "Display" "Account Code" 'boolean '(#t #f))
+ (vector "Display" "Individual tax columns" 'boolean '(#t #f))
+ (vector "Display" "Remittance amount" 'boolean '(#t #f))
+ (vector "Display" "Memo" 'boolean '(#t #f))
+ (vector "Display" "Totals" 'boolean '(#t #f))
+ (vector "Display" "Account Name" 'boolean '(#t #f))
+ (vector "Display" "Other Account Name" 'boolean '(#t #f))
+ (vector "Display" "Other Account Code" 'boolean '(#t #f))
+ (vector "Display" "Reconciled Date" 'boolean '(#t #f))
+ (vector "Display" "Subtotal Table" 'boolean '(#t #f))
+ (vector "Display" "Notes" 'boolean '(#t #f))
+ (vector "Display" "Date" 'boolean '(#t #f))
+ (vector "Sorting" "Secondary Sort Order" 'multichoice '(ascend descend))
+ (vector "Sorting" "Show Full Account Name" 'boolean '(#t #f))
+ (vector "Sorting" "Show Account Code" 'boolean '(#t #f))
+ (vector "Sorting" "Show Account Description" 'boolean '(#t #f))
+ (vector "Sorting" "Primary Subtotal for Date Key" 'multichoice '(none daily weekly monthly quarterly yearly))
+ (vector "Sorting" "Secondary Key" 'multichoice '(account-name account-code date reconciled-date reconciled-status register-order corresponding-acc-name corresponding-acc-code amount description number t-number memo notes none))
+ (vector "Sorting" "Add indenting columns" 'boolean '(#t #f))
+ (vector "Sorting" "Secondary Subtotal" 'boolean '(#t #f))
+ (vector "Sorting" "Primary Subtotal" 'boolean '(#t #f))
+ (vector "Sorting" "Secondary Subtotal for Date Key" 'multichoice '(none daily weekly monthly quarterly yearly))
+ (vector "Sorting" "Primary Key" 'multichoice '(account-name account-code date reconciled-date reconciled-status register-order corresponding-acc-name corresponding-acc-code amount description number t-number memo notes none))
+ (vector "Sorting" "Primary Sort Order" 'multichoice '(ascend descend))
+ (vector "Sorting" "Show subtotals only (hide transactional data)" 'boolean '(#t #f))
+ (vector "Filter" "Void Transactions" 'multichoice '(non-void-only void-only both))
+ (vector "Filter" "Use regular expressions for transaction filter" 'boolean '(#t #f))
+ (vector "Filter" "Use regular expressions for account name filter" 'boolean '(#t #f))
+ (vector "Filter" "Reconcile Status" 'multichoice '(all unreconciled cleared reconciled))
+ (vector "General" "Table for Exporting" 'boolean '(#t #f))
+ (vector "General" "Add options summary" 'multichoice '(no-match always never))
+ (vector "General" "Common Currency" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "3fe6dce77da24c66bdc8f8efdea7f9ac")
+ (cons 'report-name "Liabilities")
+ (cons 'options
+ (list
+ (vector "Accounts" "Show Accounts until level" 'multichoice '(all 1 2 3 4 5 6))
+ (vector "Display" "Show Totals" 'boolean '(#t #f))
+ (vector "Display" "Show long names" 'boolean '(#t #f))
+ (vector "Display" "Show Percents" 'boolean '(#t #f))
+ (vector "Display" "Sort Method" 'multichoice '(acct-code alphabetical amount))
+ (vector "General" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ )))
+ (list (cons 'report-id "5123a759ceb9483abf2182d01c140e8d")
+ (cons 'report-name "Printable Invoice")
+ (cons 'options
+ (list
+ (vector "Display" "Individual Taxes" 'boolean '(#t #f))
+ (vector "Display" "Billing Terms" 'boolean '(#t #f))
+ (vector "Display" "References" 'boolean '(#t #f))
+ (vector "Display" "Totals" 'boolean '(#t #f))
+ (vector "Display" "Invoice Notes" 'boolean '(#t #f))
+ (vector "Display" "Billing ID" 'boolean '(#t #f))
+ (vector "Display" "Payments" 'boolean '(#t #f))
+ (vector "Display" "Job Details" 'boolean '(#t #f))
+ (vector "Display Columns" "Tax Amount" 'boolean '(#t #f))
+ (vector "Display Columns" "Description" 'boolean '(#t #f))
+ (vector "Display Columns" "Discount" 'boolean '(#t #f))
+ (vector "Display Columns" "Total" 'boolean '(#t #f))
+ (vector "Display Columns" "Action" 'boolean '(#t #f))
+ (vector "Display Columns" "Taxable" 'boolean '(#t #f))
+ (vector "Display Columns" "Quantity" 'boolean '(#t #f))
+ (vector "Display Columns" "Price" 'boolean '(#t #f))
+ (vector "Display Columns" "Date" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "21d7cfc59fc74f22887596ebde7e462d")
+ (cons 'report-name "Advanced Portfolio")
+ (cons 'options
+ (list
+ (vector "Accounts" "Include accounts with no shares" 'boolean '(#t #f))
+ (vector "Display" "Show ticker symbols" 'boolean '(#t #f))
+ (vector "Display" "Show number of shares" 'boolean '(#t #f))
+ (vector "Display" "Show prices" 'boolean '(#t #f))
+ (vector "Display" "Show listings" 'boolean '(#t #f))
+ (vector "General" "How to report brokerage fees" 'multichoice '(include-in-basis include-in-gain ignore-brokerage))
+ (vector "General" "Price Source" 'multichoice '(pricedb-latest pricedb-nearest))
+ (vector "General" "Basis calculation method" 'multichoice '(average-basis fifo-basis filo-basis))
+ (vector "General" "Set preference for price list data" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "7eb3df21073d4c33920a0257da15fba5")
+ (cons 'report-name "Receipt")
+ (cons 'options
+ (list
+ )))
+ (list (cons 'report-id "e6e34fa3b6e748debde3cb3bc76d3e53")
+ (cons 'report-name "Budget Flow")
+ (cons 'options
+ (list
+ (vector "General" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ )))
+ (list (cons 'report-id "b1f15b2052c149df93e698fe85a81ea6")
+ (cons 'report-name "Expense Over Time")
+ (cons 'options
+ (list
+ (vector "Accounts" "Show Accounts until level" 'multichoice '(all 1 2 3 4 5 6))
+ (vector "Display" "Chart Type" 'multichoice '(barchart linechart))
+ (vector "Display" "Use Stacked Charts" 'boolean '(#t #f))
+ (vector "Display" "Sort Method" 'multichoice '(acct-code alphabetical amount))
+ (vector "Display" "Show table" 'boolean '(#t #f))
+ (vector "Display" "Show long account names" 'boolean '(#t #f))
+ (vector "General" "Show Average" 'multichoice '(None MonthDelta WeekDelta DayDelta))
+ (vector "General" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "General" "Step Size" 'multichoice '(DayDelta WeekDelta TwoWeekDelta MonthDelta QuarterDelta HalfYearDelta YearDelta))
+ )))
+ (list (cons 'report-id "898d78ec92854402bf76e20a36d24ade")
+ (cons 'report-name "Hello, World")
+ (cons 'options
+ (list
+ (vector "Hello, World!" "Multi Choice Option" 'multichoice '(first second third fourth))
+ (vector "Hello, World!" "Boolean Option" 'boolean '(#t #f))
+ (vector "Testing" "Crash the report" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "5e2d129f28d14df881c3e47e3053f604")
+ (cons 'report-name "Income vs. Day of Week")
+ (cons 'options
+ (list
+ (vector "Accounts" "Include Sub-Accounts" 'boolean '(#t #f))
+ (vector "Accounts" "Show Accounts until level" 'multichoice '(all 1 2 3 4 5 6))
+ (vector "Display" "Show Totals" 'boolean '(#t #f))
+ (vector "General" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ )))
+ (list (cons 'report-id "faf410e8f8da481fbc09e4763da40bcc")
+ (cons 'report-name "Liabilities Over Time")
+ (cons 'options
+ (list
+ (vector "Accounts" "Show Accounts until level" 'multichoice '(all 1 2 3 4 5 6))
+ (vector "Display" "Chart Type" 'multichoice '(barchart linechart))
+ (vector "Display" "Use Stacked Charts" 'boolean '(#t #f))
+ (vector "Display" "Sort Method" 'multichoice '(acct-code alphabetical amount))
+ (vector "Display" "Show table" 'boolean '(#t #f))
+ (vector "Display" "Show long account names" 'boolean '(#t #f))
+ (vector "General" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "General" "Step Size" 'multichoice '(DayDelta WeekDelta TwoWeekDelta MonthDelta QuarterDelta HalfYearDelta YearDelta))
+ )))
+ (list (cons 'report-id "0b81a3bdfd504aff849ec2e8630524bc")
+ (cons 'report-name "Income Statement")
+ (cons 'options
+ (list
+ (vector "Commodities" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "Commodities" "Show Exchange Rates" 'boolean '(#t #f))
+ (vector "Commodities" "Show Foreign Currencies" 'boolean '(#t #f))
+ (vector "Accounts" "Levels of Subaccounts" 'multichoice '(all 1 2 3 4 5 6))
+ (vector "Accounts" "Flatten list to depth limit" 'boolean '(#t #f))
+ (vector "Entries" "Closing Entries pattern is case-sensitive" 'boolean '(#t #f))
+ (vector "Entries" "Closing Entries Pattern is regular expression" 'boolean '(#t #f))
+ (vector "Display" "Display as a two column report" 'boolean '(#t #f))
+ (vector "Display" "Show accounting-style rules" 'boolean '(#t #f))
+ (vector "Display" "Display accounts as hyperlinks" 'boolean '(#t #f))
+ (vector "Display" "Parent account balances" 'multichoice '(immediate-bal recursive-bal omit-bal))
+ (vector "Display" "Display in standard, income first, order" 'boolean '(#t #f))
+ (vector "Display" "Include accounts with zero total balances" 'boolean '(#t #f))
+ (vector "Display" "Label the revenue section" 'boolean '(#t #f))
+ (vector "Display" "Parent account subtotals" 'multichoice '(t f canonically-tabbed))
+ (vector "Display" "Include expense total" 'boolean '(#t #f))
+ (vector "Display" "Include trading accounts total" 'boolean '(#t #f))
+ (vector "Display" "Omit zero balance figures" 'boolean '(#t #f))
+ (vector "Display" "Include revenue total" 'boolean '(#t #f))
+ (vector "Display" "Label the expense section" 'boolean '(#t #f))
+ (vector "Display" "Label the trading accounts section" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "415cd38d39054d9e9c4040455290c2b1")
+ (cons 'report-name "Budget Chart")
+ (cons 'options
+ (list
+ (vector "Accounts" "Levels of Subaccounts" 'multichoice '(all 1 2 3 4 5 6))
+ (vector "Display" "Chart Type" 'multichoice '(bars lines))
+ (vector "Display" "Running Sum" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "f8748b813fab4220ba26e743aedf38da")
+ (cons 'report-name "Cash Flow")
+ (cons 'options
+ (list
+ (vector "Accounts" "Always show sub-accounts" 'boolean '(#t #f))
+ (vector "Accounts" "Include Trading Accounts in report" 'boolean '(#t #f))
+ (vector "Accounts" "Account Display Depth" 'multichoice '(all 1 2 3 4 5 6))
+ (vector "General" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "General" "Show Exchange Rates" 'boolean '(#t #f))
+ (vector "General" "Show Full Account Names" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "65135608f2014c6ca8412793a8cdf169")
+ (cons 'report-name "Welcome to GnuCash")
+ (cons 'options
+ (list
+ )))
+ (list (cons 'report-id "d7d1e53505ee4b1b82efad9eacedaea0")
+ (cons 'report-name "Vendor Report")
+ (cons 'options
+ (list
+ (vector "Display Columns" "Tax" 'boolean '(#t #f))
+ (vector "Display Columns" "Description" 'boolean '(#t #f))
+ (vector "Display Columns" "Amount" 'boolean '(#t #f))
+ (vector "Display Columns" "Debits" 'boolean '(#t #f))
+ (vector "Display Columns" "Type" 'boolean '(#t #f))
+ (vector "Display Columns" "Due Date" 'boolean '(#t #f))
+ (vector "Display Columns" "Sale" 'boolean '(#t #f))
+ (vector "Display Columns" "Reference" 'boolean '(#t #f))
+ (vector "Display Columns" "Credits" 'boolean '(#t #f))
+ (vector "Display Columns" "Date" 'boolean '(#t #f))
+ (vector "General" "Due or Post Date" 'multichoice '(duedate postdate))
+ (vector "__reg" "reverse?" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "5518ac227e474f47a34439f2d4d049de")
+ (cons 'report-name "Job Report")
+ (cons 'options
+ (list
+ (vector "Display Columns" "Description" 'boolean '(#t #f))
+ (vector "Display Columns" "Amount" 'boolean '(#t #f))
+ (vector "Display Columns" "Type" 'boolean '(#t #f))
+ (vector "Display Columns" "Due Date" 'boolean '(#t #f))
+ (vector "Display Columns" "Reference" 'boolean '(#t #f))
+ (vector "Display Columns" "Date" 'boolean '(#t #f))
+ (vector "__reg" "reverse?" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "e5fa5ce805e840ecbeca4dba3fa4ead9")
+ (cons 'report-name "Budget Profit & Loss")
+ (cons 'options
+ (list
+ (vector "Commodities" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "Commodities" "Show Exchange Rates" 'boolean '(#t #f))
+ (vector "Commodities" "Show Foreign Currencies" 'boolean '(#t #f))
+ (vector "Accounts" "Levels of Subaccounts" 'multichoice '(all 1 2 3 4 5 6))
+ (vector "Accounts" "Flatten list to depth limit" 'boolean '(#t #f))
+ (vector "Display" "Display as a two column report" 'boolean '(#t #f))
+ (vector "Display" "Show accounting-style rules" 'boolean '(#t #f))
+ (vector "Display" "Display accounts as hyperlinks" 'boolean '(#t #f))
+ (vector "Display" "Parent account balances" 'multichoice '(immediate-bal recursive-bal omit-bal))
+ (vector "Display" "Display in standard, income first, order" 'boolean '(#t #f))
+ (vector "Display" "Include accounts with zero total balances" 'boolean '(#t #f))
+ (vector "Display" "Label the revenue section" 'boolean '(#t #f))
+ (vector "Display" "Parent account subtotals" 'multichoice '(t f canonically-tabbed))
+ (vector "Display" "Include expense total" 'boolean '(#t #f))
+ (vector "Display" "Omit zero balance figures" 'boolean '(#t #f))
+ (vector "Display" "Include revenue total" 'boolean '(#t #f))
+ (vector "Display" "Label the expense section" 'boolean '(#t #f))
+ (vector "General" "Report for range of budget periods" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "e533c998186b11e1b2e2001558291366")
+ (cons 'report-name "Income & Expense Linechart")
+ (cons 'options
+ (list
+ (vector "Display" "Grid" 'boolean '(#t #f))
+ (vector "Display" "Data markers?" 'boolean '(#t #f))
+ (vector "Display" "Show Income/Expense" 'boolean '(#t #f))
+ (vector "Display" "Show table" 'boolean '(#t #f))
+ (vector "Display" "Show Net Profit" 'boolean '(#t #f))
+ (vector "General" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "General" "Step Size" 'multichoice '(DayDelta WeekDelta TwoWeekDelta MonthDelta QuarterDelta HalfYearDelta YearDelta))
+ )))
+ (list (cons 'report-id "22104e02654c4adba844ee75a3f8d173")
+ (cons 'report-name "Register")
+ (cons 'options
+ (list
+ (vector "Display" "Description" 'boolean '(#t #f))
+ (vector "Display" "Account" 'boolean '(#t #f))
+ (vector "Display" "Amount" 'multichoice '(single double))
+ (vector "Display" "Num" 'boolean '(#t #f))
+ (vector "Display" "Running Balance" 'boolean '(#t #f))
+ (vector "Display" "Shares" 'boolean '(#t #f))
+ (vector "Display" "Lot" 'boolean '(#t #f))
+ (vector "Display" "Memo" 'boolean '(#t #f))
+ (vector "Display" "Totals" 'boolean '(#t #f))
+ (vector "Display" "Price" 'boolean '(#t #f))
+ (vector "Display" "Date" 'boolean '(#t #f))
+ (vector "Display" "Value" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "d8b63264186b11e19038001558291366")
+ (cons 'report-name "Net Worth Linechart")
+ (cons 'options
+ (list
+ (vector "Display" "Grid" 'boolean '(#t #f))
+ (vector "Display" "Show Asset & Liability" 'boolean '(#t #f))
+ (vector "Display" "Data markers?" 'boolean '(#t #f))
+ (vector "Display" "Show table" 'boolean '(#t #f))
+ (vector "Display" "Show Net Worth" 'boolean '(#t #f))
+ (vector "General" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "General" "Step Size" 'multichoice '(DayDelta WeekDelta TwoWeekDelta MonthDelta QuarterDelta HalfYearDelta YearDelta))
+ )))
+ (list (cons 'report-id "1d241609fd4644caad765c95be20ff4c")
+ (cons 'report-name "Price")
+ (cons 'options
+ (list
+ (vector "Display" "Marker" 'multichoice '(diamond circle square cross plus dash filleddiamond filledcircle filledsquare))
+ (vector "Price" "Invert prices" 'boolean '(#t #f))
+ (vector "Price" "Price Source" 'multichoice '(weighted-average actual-transactions pricedb))
+ (vector "General" "Step Size" 'multichoice '(DayDelta WeekDelta TwoWeekDelta MonthDelta QuarterDelta HalfYearDelta YearDelta))
+ )))
+ (list (cons 'report-id "ecc35ea9dbfa4e20ba389fc85d59cb69")
+ (cons 'report-name "Budget Balance Sheet")
+ (cons 'options
+ (list
+ (vector "Commodities" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ (vector "Commodities" "Show Exchange Rates" 'boolean '(#t #f))
+ (vector "Commodities" "Show Foreign Currencies" 'boolean '(#t #f))
+ (vector "Accounts" "Levels of Subaccounts" 'multichoice '(all 1 2 3 4 5 6))
+ (vector "Accounts" "Flatten list to depth limit" 'boolean '(#t #f))
+ (vector "Display" "Show accounting-style rules" 'boolean '(#t #f))
+ (vector "Display" "Display accounts as hyperlinks" 'boolean '(#t #f))
+ (vector "Display" "Parent account balances" 'multichoice '(immediate-bal recursive-bal omit-bal))
+ (vector "Display" "Include assets total" 'boolean '(#t #f))
+ (vector "Display" "Include accounts with zero total balances" 'boolean '(#t #f))
+ (vector "Display" "Parent account subtotals" 'multichoice '(t f canonically-tabbed))
+ (vector "Display" "Include liabilities total" 'boolean '(#t #f))
+ (vector "Display" "Label the liabilities section" 'boolean '(#t #f))
+ (vector "Display" "Include new/existing totals" 'boolean '(#t #f))
+ (vector "Display" "Omit zero balance figures" 'boolean '(#t #f))
+ (vector "Display" "Label the assets section" 'boolean '(#t #f))
+ (vector "Display" "Include equity total" 'boolean '(#t #f))
+ (vector "Display" "Label the equity section" 'boolean '(#t #f))
+ (vector "General" "Single column Balance Sheet" 'boolean '(#t #f))
+ )))
+ (list (cons 'report-id "5c7fd8a1fe9a4cd38884ff54214aa88a")
+ (cons 'report-name "Assets")
+ (cons 'options
+ (list
+ (vector "Accounts" "Show Accounts until level" 'multichoice '(all 1 2 3 4 5 6))
+ (vector "Display" "Show Totals" 'boolean '(#t #f))
+ (vector "Display" "Show long names" 'boolean '(#t #f))
+ (vector "Display" "Show Percents" 'boolean '(#t #f))
+ (vector "Display" "Sort Method" 'multichoice '(acct-code alphabetical amount))
+ (vector "General" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ )))
+ (list (cons 'report-id "9bf1892805cb4336be6320fe48ce5446")
+ (cons 'report-name "Expense Accounts")
+ (cons 'options
+ (list
+ (vector "Accounts" "Show Accounts until level" 'multichoice '(all 1 2 3 4 5 6))
+ (vector "Display" "Show Totals" 'boolean '(#t #f))
+ (vector "Display" "Show long names" 'boolean '(#t #f))
+ (vector "Display" "Show Percents" 'boolean '(#t #f))
+ (vector "Display" "Sort Method" 'multichoice '(acct-code alphabetical amount))
+ (vector "General" "Show Average" 'multichoice '(None YearDelta MonthDelta WeekDelta))
+ (vector "General" "Price Source" 'multichoice '(average-cost weighted-average pricedb-latest pricedb-nearest))
+ )))
+ ))
commit 694d0f06133826ecc036b026fbf0290455206b13
Author: Christopher Lam <christopher.lck at gmail.com>
Date: Tue Jul 10 23:31:36 2018 +0800
[budget-flow] fix report-title not defined
this report uses reportname instead
diff --git a/gnucash/report/standard-reports/budget-flow.scm b/gnucash/report/standard-reports/budget-flow.scm
index f718968..d15b81e 100644
--- a/gnucash/report/standard-reports/budget-flow.scm
+++ b/gnucash/report/standard-reports/budget-flow.scm
@@ -295,7 +295,7 @@
(gnc:html-document-add-object!
doc
(gnc:html-make-no-account-warning
- report-title (gnc:report-id report-obj))))
+ reportname (gnc:report-id report-obj))))
((not budget-valid?)
;; No budget selected.
commit 57c6f175b442988c2135dc250c39b4c7d8e726be
Author: Christopher Lam <christopher.lck at gmail.com>
Date: Tue Jul 10 23:30:55 2018 +0800
[html-chart] num-columns return 0 for empty-table
this commit fixes whereby data is '() indicating no columns
diff --git a/gnucash/report/report-system/html-table.scm b/gnucash/report/report-system/html-table.scm
index 41a615f..97615b6 100644
--- a/gnucash/report/report-system/html-table.scm
+++ b/gnucash/report/report-system/html-table.scm
@@ -326,7 +326,7 @@
(record-modifier <html-table> 'num-rows))
(define (gnc:html-table-num-columns table)
- (apply max (map length (gnc:html-table-data table))))
+ (apply max (cons 0 (map length (gnc:html-table-data table)))))
(define (gnc:html-table-append-row/markup! table markup newrow)
(let ((rownum (gnc:html-table-append-row! table newrow)))
commit e2907844be26d61914385e3932fe1de2ca31b588
Author: Christopher Lam <christopher.lck at gmail.com>
Date: Tue Jul 10 23:30:26 2018 +0800
[customer-summary] prevents crash on empty-book with no accounts
diff --git a/gnucash/report/business-reports/customer-summary.scm b/gnucash/report/business-reports/customer-summary.scm
index b0384be..f6e05c5 100644
--- a/gnucash/report/business-reports/customer-summary.scm
+++ b/gnucash/report/business-reports/customer-summary.scm
@@ -699,7 +699,7 @@
(expense-accounts (opt-val pagename-expenseaccounts optname-expenseaccounts))
(income-accounts (opt-val pagename-incomeaccounts optname-incomeaccounts))
(all-accounts (append income-accounts expense-accounts))
- (book (gnc-account-get-book (car all-accounts)))
+ (book (gnc-get-current-book))
(date-format (gnc:options-fancy-date book))
(type (opt-val "__reg" "owner-type"))
(reverse? (opt-val "__reg" "reverse?"))
Summary of changes:
borrowed/jenny/jenny.c | 1806 ++++++++++++++++++++
gnucash/gschemas/org.gnucash.gschema.xml.in | 2 +-
gnucash/gtkbuilder/dialog-preferences.glade | 2 +-
.../report/business-reports/customer-summary.scm | 2 +-
gnucash/report/report-system/html-table.scm | 2 +-
gnucash/report/standard-reports/budget-flow.scm | 2 +-
.../report/standard-reports/test/CMakeLists.txt | 1 +
.../standard-reports/test/test-stress-options.scm | 339 ++++
.../test/test-stress-optionslist.scm | 972 +++++++++++
9 files changed, 3123 insertions(+), 5 deletions(-)
create mode 100644 borrowed/jenny/jenny.c
create mode 100644 gnucash/report/standard-reports/test/test-stress-options.scm
create mode 100644 gnucash/report/standard-reports/test/test-stress-optionslist.scm
More information about the gnucash-changes
mailing list