#include "generation.h"
extern int maxbinsz;
#define CASENUM 19
#define CTYPENUM 4
char * data_set[CASENUM][CTYPENUM] = {
{"\"0\"", "\"zero\"", "0", "0"},
{"\"1\"", "\"one\"", "1", "1.1"},
{"\"2\"", "\"two\"", "2", "2.2"},
{"\"3\"", "\"three\"", "3", "3.3"},
{"\"4\"", "\"four\"", "4", "4.4"},
{"\"5\"", "\"five\"", "5", "5.5"},
{"\"6\"", "\"six\"", "6", "6.6"},
{"\"7\"", "\"seven\"", "7", "7.7"},
{"\"8\"", "\"eight\"", "8", "8.8"},
{"\"9\"", "\"nine\"", "9", "9.9"},
{"\"A\"", "\"eleven\"", "11", "11.1"},
{"\"B\"", "\"twenty-two\"", "22", "22.2"},
{"\"C\"", "\"thirty-three\"", "33", "33.3"},
{"\"D\"", "\"forty-four\"", "44", "44.4"},
{"\"E\"", "\"fifty-five\"", "55", "55.5"},
{"\"F\"", "\"sixty-six\"", "66", "66.6"},
{"\"G\"", "\"seventy-seven\"", "77", "77.7"},
{"\"H\"", "\"eighty-eight\"", "88", "88.8"},
{"\"I\"", "\"ninety-nine\"", "99", "99.9"}};
static FILE *test_file;
static int round = 0;
static int test_indent_level = 0;
void generate_verification(FILE *, char *);
static void pr_test(char *, ...);
static void pr_test_comment(char *, ...);
static void callback_function_enter_entop(ENTITY *);
static void callback_function_exit_entop(ENTITY *);
static void callback_function_attrop(ENTITY *, ATTRIBUTE *, int, int);
static void declare_record_instances_enter_entop(ENTITY *);
static void define_records_enter_entop(ENTITY *);
static void define_records_exit_entop(ENTITY *);
static void define_records_fields_attrop(ENTITY *, ATTRIBUTE *, int, int);
static void initialize_database_enter_entop(ENTITY *);
static void initialize_index_enter_entop(DB_INDEX *);
static void insertion_test_enter_entop(ENTITY *);
static void insertion_test_exit_entop(ENTITY *);
static void insertion_test_attrop(ENTITY *, ATTRIBUTE *, int, int);
static void retrieval_test_enter_entop(ENTITY *);
static void retrieval_test_exit_entop(ENTITY *);
static void retrieval_test_attrop(ENTITY *, ATTRIBUTE *, int, int);
static void invoke_full_iteration_enter_entop(ENTITY *);
static void invoke_full_iteration_exit_entop(ENTITY *);
static void invoke_query_iteration_idxop(DB_INDEX *);
static void deletion_test_enter_entop(ENTITY *);
static void deletion_test_exit_entop(ENTITY *);
static void close_secondary_test_idxop(DB_INDEX *);
static void close_primary_test_enter_entop(ENTITY *);
static void remove_secondary_test_idxop(DB_INDEX *);
static void remove_primary_test_enter_entop(ENTITY *);
void
generate_verification(vfile, hfilename)
FILE *vfile;
char *hfilename;
{
test_file = vfile;
pr_test_comment(
"Simple test for a Berkeley DB implementation \n\
generated from SQL DDL by db_sql \n\
");
pr_test("#include <assert.h>\n");
pr_test("#include <math.h>\n\n");
pr_test("\n#include \"%s\"\n\n", hfilename);
if (maxbinsz != 0) {
pr_test_comment("Test data for raw binary types");
pr_test("#define MAXBINSZ %d\n\n", maxbinsz);
pr_test("char binary_data[%d][MAXBINSZ];\n", CASENUM);
}
pr_test("int count_iteration;\n\n");
pr_test_comment("Test data for input record");
iterate_over_entities(&define_records_enter_entop,
&define_records_exit_entop,
NULL);
if (maxbinsz != 0) {
pr_test_comment("A very simple binary comparison function");
pr_test(
"int compare_binary(char *p, int len, int dem) \n\
{ \n\
if (memcmp(p, binary_data[dem], len) == 0) { \n\
return 0; \n\
} else { \n\
return 1; \n\
} \n\
} \n\
\n\
");
}
pr_test_comment(
"These are the iteration callback functions. One is defined per \n\
database(table). They are used for both full iterations and for \n\
secondary index queries. When a retrieval returns multiple records, \n\
as in full iteration over an entire database, one of these functions \n\
is called for each record found");
iterate_over_entities(&callback_function_enter_entop,
&callback_function_exit_entop,
&callback_function_attrop);
pr_test(
" \n\
main(int argc, char **argv) \n\
{ \n\
int i; \n\
int j; \n\
int ret; \n\
int occurence; \n\
\n\
");
test_indent_level++;
iterate_over_entities(&declare_record_instances_enter_entop,
NULL,
NULL);
iterate_over_entities(&initialize_database_enter_entop,
NULL,
NULL);
iterate_over_indexes(&initialize_index_enter_entop);
pr_test("\n");
if (maxbinsz != 0) {
pr_test_comment("Fill the binary test data with random values");
pr_test(
"for (i = 0; i < %d; i++) \n\
for (j = 0; j < MAXBINSZ; j++) \n\
binary_data[i][j] = rand(); \n\
\n\
",
CASENUM);
}
pr_test_comment(
"Use the convenience method to initialize the environment. \n\
The initializations for each entity and environment can be \n\
done discretely if you prefer, but this is the easy way.");
pr_test(
"ret = initialize_%s_environment(); \n\
if (ret != 0) { \n\
printf(\"Initialize error\"); \n\
return ret; \n\
} \n\
\n\
",
the_schema.environment.name);
pr_test("\n");
pr_test_comment(
"Now that everything is initialized, insert some records \n\
into each database, retrieve and verify them");
pr_test("for (i = 0; i < %d; i++) {\n", CASENUM);
test_indent_level++;
pr_test_comment(
"First, insert some records into each database using \n\
the ...insert_fields functions. These functions take \n\
each field of the record as a separate argument");
iterate_over_entities(&insertion_test_enter_entop,
&insertion_test_exit_entop,
&insertion_test_attrop);
pr_test("\n");
pr_test_comment(
"Next, retrieve the records just inserted, looking them up \n\
by their key values");
iterate_over_entities(&retrieval_test_enter_entop,
&retrieval_test_exit_entop,
&retrieval_test_attrop);
test_indent_level--;
pr_test("}\n");
pr_test("\n");
pr_test_comment(
"Now try iterating over every record, using the ...full_iteration \n\
functions for each database. For each record found, the \n\
appropriate ...iteration_callback_test function will be invoked \n\
(these are defined above).");
iterate_over_entities(&invoke_full_iteration_enter_entop,
&invoke_full_iteration_exit_entop,
NULL);
pr_test("\n");
pr_test_comment(
"For the secondary indexes, query for the known keys. This also \n\
results in the ...iteration_callback_test function's being called \n\
for each record found.");
iterate_over_indexes(&invoke_query_iteration_idxop);
pr_test("\n");
pr_test_comment(
"Now delete a record from each database using its primary key.");
for (round = 0; round < CASENUM; round++) {
iterate_over_entities(&deletion_test_enter_entop,
&deletion_test_exit_entop,
NULL);
}
pr_test("\n");
test_indent_level--;
pr_test("exit_error:\n");
test_indent_level++;
pr_test_comment("Close the secondary index databases");
iterate_over_indexes(&close_secondary_test_idxop);
pr_test("\n");
pr_test_comment("Close the primary databases");
iterate_over_entities(&close_primary_test_enter_entop,
NULL,
NULL);
pr_test("\n");
pr_test_comment("Delete the secondary index databases");
iterate_over_indexes(&remove_secondary_test_idxop);
pr_test("\n");
pr_test_comment("Delete the primary databases");
iterate_over_entities(&remove_primary_test_enter_entop,
NULL,
NULL);
if (the_schema.environment.transactional) {
pr_test(
" \n\
if (ret != 0) \n\
%s_txnp->abort(%s_txnp); \n\
else \n\
%s_txnp->commit(%s_txnp, 0); \n\
",
the_schema.environment.name,
the_schema.environment.name,
the_schema.environment.name,
the_schema.environment.name);
}
pr_test("\n");
pr_test_comment("Finally, close the environment");
pr_test(
"if (ret = %s_envp->close(%s_envp, 0)) \n\
return ret;\n",
the_schema.environment.name, the_schema.environment.name);
pr_test("\n");
pr_test(
"if (ret == 0) \n\
printf(\"*****WELL DONE!*****\\n\"); \n\
");
pr_test("return ret;\n");
test_indent_level--;
pr_test("}\n");
}
static void
pr_test(char *fmt, ...)
{
va_list ap;
char *s;
static int enable_indent = 1;
s = prepare_string(fmt,
enable_indent ? test_indent_level : 0,
0);
va_start(ap, fmt);
vfprintf(test_file, s, ap);
va_end(ap);
if (s[strlen(s) - 1] == '\n')
enable_indent = 1;
else
enable_indent = 0;
}
static void
pr_test_comment(char *fmt, ...)
{
va_list ap;
char *s;
s = prepare_string(fmt, test_indent_level, 1);
va_start(ap, fmt);
vfprintf(test_file, s, ap);
va_end(ap);
}
static char *
data_value_for_type(ATTR_TYPE *t)
{
char *c_type = t->c_type;
char *bin_array = NULL;
const char *bin_name = "binary_data[";
size_t bin_len = 0;
int count_bit;
int quote;
if (is_string(t)) {
if (t->array_dim < 12)
return (data_set[round][0]);
return (data_set[round][1]);
} else if (is_array(t)) {
for (quote = round, count_bit = 1; quote > 0;
quote/=10, count_bit++);
bin_len = strlen(bin_name) + count_bit + 2;
bin_array = (char *)malloc(bin_len);
memset(bin_array, 0, bin_len);
snprintf(bin_array, bin_len, "%s%d%c", bin_name, round, ']');
return (bin_array);
} else if (strcmp(c_type, "char") == 0 ||
strcmp(c_type, "short") == 0 ||
strcmp(c_type, "int") == 0 ||
strcmp(c_type, "long") == 0) {
return (data_set[round][2]);
} else if (strcmp(c_type, "float") == 0 ||
strcmp(c_type, "double") == 0) {
return (data_set[round][3]);
} else {
fprintf(stderr,
"Unexpected C type in schema: %s", c_type);
assert(0);
}
return NULL;
}
static void
define_records_enter_entop(ENTITY *e)
{
pr_test("%s_data %s_record_array[] = {\n",
e->name, e->name);
test_indent_level++;
for (round = 0; round < CASENUM; round++) {
pr_test("{");
iterate_over_attributes(e, &define_records_fields_attrop);
pr_test("}");
if (round < CASENUM -1)
pr_test(",");
pr_test("\n");
}
test_indent_level--;
}
static void
define_records_exit_entop(ENTITY *e)
{
COMPQUIET(e, NULL);
pr_test("};\n\n");
}
static void
define_records_fields_attrop(ENTITY *e, ATTRIBUTE *a, int first, int last)
{
int get_row_num;
COMPQUIET(first, 0);
if ((e->primary_key != a) && round > 9)
get_row_num = round - 10;
else
get_row_num = round;
if (is_array(a->type) && !is_string(a->type)) {
pr_test("{0}");
} else if (is_string(a->type)) {
if (a->type->array_dim < 12)
pr_test(data_set[get_row_num][0]);
else
pr_test(data_set[get_row_num][1]);
} else if (strcmp(a->type->c_type, "char") == 0 ||
strcmp(a->type->c_type, "short") == 0 ||
strcmp(a->type->c_type, "int") == 0 ||
strcmp(a->type->c_type, "long") == 0) {
pr_test(data_set[get_row_num][2]);
} else if (strcmp(a->type->c_type, "float") == 0 ||
strcmp(a->type->c_type, "double") == 0) {
pr_test(data_set[get_row_num][3]);
} else {
fprintf(stderr,
"Unexpected C type in schema: %s", a->type->c_type);
assert(0);
}
if (!last)
pr_test(", ");
}
static void
declare_record_instances_enter_entop(ENTITY *e)
{
pr_test("%s_data %s_record;\n", e->name, e->name);
}
static void
initialize_database_enter_entop(ENTITY *e)
{
pr_test("%s_dbp = NULL;\n", e->name);
}
static void
initialize_index_enter_entop(DB_INDEX *idx)
{
pr_test("%s_dbp = NULL;\n", idx->name);
}
static void
invoke_full_iteration_enter_entop(ENTITY *e)
{
pr_test("count_iteration = 0;\n");
pr_test(
"ret = %s_full_iteration(%s_dbp, %s%s&%s_iteration_callback_test, \n\
\"retrieval of %s record through full iteration\");\n",
e->name, e->name,
e->transactional ? the_schema.environment.name : "",
e->transactional ? "_txnp, " : "",
e->name, e->name);
}
static void
invoke_full_iteration_exit_entop(ENTITY *e)
{
pr_test(
"if (ret != 0) { \n\
printf(\"Full Iteration Error\\n\"); \n\
goto exit_error; \n\
} \n\
printf(\"%s full iteration: %%d\\n\", count_iteration); \n\
assert(count_iteration == %d); \n\
\n\
",
e->name, CASENUM);
}
static void
invoke_query_iteration_idxop(DB_INDEX *idx)
{
ATTRIBUTE *a = idx->attribute;
pr_test("for (i = 0; i < %d; i++) {\n", CASENUM);
test_indent_level++;
pr_test("count_iteration = 0;\n");
pr_test("%s_query_iteration(%s_dbp, %s%s",
idx->name, idx->name,
idx->primary->transactional ? the_schema.environment.name : "",
idx->primary->transactional ? "_txnp, " : "");
pr_test("%s_record_array[i].%s",
idx->primary->name, a->name);
pr_test(
",\n &%s_iteration_callback_test, \n\
\"retrieval of %s record through %s query\");\n\n",
idx->primary->name, idx->primary->name, idx->name);
pr_test(
"printf(\"%s_record_array[%%d].%s: %%d\\n\", i, count_iteration);\n",
idx->primary->name, a->name,
idx->primary->name, a->name);
if (is_array(a->type) && !is_string(a->type)) {
pr_test("assert(count_iteration == 1);\n");
} else {
pr_test("occurence = 0;\n");
pr_test("for (j = 0; j < %d; j++) {\n", CASENUM);
test_indent_level++;
if (is_string(a->type)) {
pr_test(
"if (strcmp(%s_record_array[j].%s, %s_record_array[i].%s) == 0) {\n",
idx->primary->name, a->name,
idx->primary->name, a->name);
} else if ((strcmp(a->type->c_type, "float") == 0) ||
(strcmp(a->type->c_type, "double") == 0)) {
pr_test(
"if (fabs(%s_record_array[j].%s - %s_record_array[i].%s) <= 0.00001) {\n",
idx->primary->name, a->name,
idx->primary->name, a->name);
} else {
pr_test(
"if (%s_record_array[j].%s == %s_record_array[i].%s) {\n",
idx->primary->name, a->name,
idx->primary->name, a->name);
}
test_indent_level++;
pr_test("occurence++;\n");
test_indent_level--;
pr_test("}\n");
test_indent_level--;
pr_test("}\n");
pr_test("assert(count_iteration == occurence);\n");
}
test_indent_level--;
pr_test("}\n\n");
}
static void
insertion_test_enter_entop(ENTITY *e)
{
pr_test("ret = %s_insert_fields( %s_dbp, %s%s",
e->name, e->name,
e->transactional ? the_schema.environment.name : "",
e->transactional ? "_txnp, " : "");
}
static void
insertion_test_exit_entop(ENTITY *e)
{
pr_test(
"if (ret != 0) { \n\
printf(\"ERROR IN INSERT NO.%%d record in %s_dbp\\n\", i); \n\
goto exit_error; \n\
} \n\
\n\
",
e->name);
}
static void
insertion_test_attrop(ENTITY *e, ATTRIBUTE *a, int first, int last)
{
if (first)
test_indent_level++;
if (is_array(a->type) && !is_string(a->type))
pr_test("binary_data[i]");
else
pr_test("%s_record_array[i].%s", e->name, a->name);
if (!last)
pr_test(", \n");
else {
pr_test(");\n");
test_indent_level--;
}
}
static void
callback_function_enter_entop(ENTITY *e)
{
pr_test(
"void %s_iteration_callback_test(void *msg, %s_data *%s_record) \n\
{ \n\
",
e->name, e->name, e->name);
test_indent_level++;
pr_test(
"int i; \n\
int same = 0; \n\
\n\
for (i = 0; i < %d; i++) { \n\
",
CASENUM);
test_indent_level++;
}
static void
callback_function_exit_entop(ENTITY *e)
{
COMPQUIET(e, NULL);
pr_test(") {\n");
test_indent_level--;
pr_test(
"same = 1; \n\
break; \n\
");
test_indent_level--;
pr_test("}\n");
test_indent_level--;
pr_test("}\n\n");
pr_test(
"if (same == 0) \n\
assert(0); \n\
else \n\
count_iteration++; \n\
");
test_indent_level--;
pr_test("}\n\n");
}
static void
callback_function_attrop(ENTITY *e, ATTRIBUTE *a, int first, int last)
{
if (first)
pr_test("if (");
if (is_array(a->type) && !is_string(a->type)) {
pr_test("(compare_binary(%s_record->%s, %s, i) == 0) ",
e->name, a->name, array_dim_name(e, a), a->name);
} else if (is_string(a->type)) {
pr_test(
"(strcmp(%s_record->%s, %s_record_array[i].%s) == 0) ",
e->name, a->name, e->name, a->name);
} else if ((strcmp(a->type->c_type, "float") == 0) ||
(strcmp(a->type->c_type, "double") == 0)) {
pr_test(
"(fabs(%s_record->%s - %s_record_array[i].%s) <= 0.00001) ",
e->name, a->name, e->name, a->name);
} else {
pr_test("(%s_record->%s == %s_record_array[i].%s) ",
e->name, a->name, e->name, a->name);
}
if (!last)
pr_test("&&\n");
if (first)
test_indent_level += 2;
}
static void
retrieval_test_enter_entop(ENTITY *e)
{
pr_test("ret = get_%s_data( %s_dbp, ", e->name, e->name);
if (e->transactional)
pr_test("%s_txnp, ", the_schema.environment.name);
if (is_array(e->primary_key->type) && !is_string(e->primary_key->type))
pr_test("binary_data[i], ");
else
pr_test("%s_record_array[i].%s, ",
e->name, e->primary_key->name);
pr_test("&%s_record);\n", e->name);
pr_test(
"if (ret != 0) { \n\
printf(\"ERROR IN RETRIEVE NO.%%d record in %s_dbp\\n\", i); \n\
goto exit_error; \n\
} \n\
\n\
",
e->name);
}
static void
retrieval_test_exit_entop(ENTITY *e)
{
COMPQUIET(e, NULL);
pr_test("\n");
}
static void
retrieval_test_attrop(ENTITY *e, ATTRIBUTE *a, int first, int last)
{
COMPQUIET(first, 0);
COMPQUIET(last, 0);
pr_test("assert(");
if (is_array(a->type) && !is_string(a->type)) {
pr_test("compare_binary(%s_record.%s, %s, i) == 0);\n",
e->name, a->name, array_dim_name(e, a));
} else if (is_string(a->type)) {
pr_test(
"strcmp(%s_record.%s, %s_record_array[i].%s) == 0);\n",
e->name, a->name, e->name, a->name);
} else if ((strcmp(a->type->c_type, "float") == 0) ||
(strcmp(a->type->c_type, "double") == 0)) {
pr_test(
"(fabs(%s_record.%s - %s_record_array[i].%s) <= 0.00001));\n",
e->name, a->name, e->name, a->name);
} else {
pr_test("%s_record.%s == %s_record_array[i].%s);\n",
e->name, a->name, e->name, a->name);
}
}
static void
deletion_test_enter_entop(ENTITY *e)
{
pr_test("ret = delete_%s_key( %s_dbp, %s%s%s);\n",
e->name, e->name,
e->transactional ? the_schema.environment.name : "",
e->transactional ? "_txnp, " : "",
data_value_for_type(e->primary_key->type));
}
static void
deletion_test_exit_entop(ENTITY *e)
{
ATTRIBUTE *a = e->primary_key;
char *key;
char * return_value;
int i, column;
key = data_value_for_type(a->type);
return_value = "0";
i = 0;
column = 0;
if (is_string(a->type)) {
if (a->type->array_dim < 12)
column = 0;
else
column = 1;
} else if (is_array(a->type)) {
column = CTYPENUM;
} else if (strcmp(a->type->c_type, "char") == 0 ||
strcmp(a->type->c_type, "short") == 0 ||
strcmp(a->type->c_type, "int") == 0 ||
strcmp(a->type->c_type, "long") == 0) {
column = 2;
} else if (strcmp(a->type->c_type, "float") == 0 ||
strcmp(a->type->c_type, "double") == 0) {
column = 3;
} else {
fprintf(stderr,
"Unexpected C type in schema: %s", a->type->c_type);
assert(0);
}
if (column != CTYPENUM) {
while (i < round) {
if (strcmp(data_set[i][column], key) == 0) {
return_value = "DB_NOTFOUND";
break;
}
i++;
}
}
pr_test(
"if (ret == %s) \n\
ret = 0; \n\
else { \n\
printf(\"ERROR IN DELETE NO.%d record in %s_dbp\\n\"); \n\
goto exit_error; \n\
}\n\n",
return_value, round + 1, e->name);
}
static void
close_primary_test_enter_entop(ENTITY *e)
{
pr_test(
"if (%s_dbp != NULL) \n\
%s_dbp->close(%s_dbp, 0); \n\
\n\
",
e->name, e->name, e->name);
}
static void
close_secondary_test_idxop(DB_INDEX *idx)
{
pr_test(
"if (%s_dbp != NULL) \n\
%s_dbp->close(%s_dbp, 0); \n\
\n\
",
idx->name, idx->name, idx->name);
}
static void
remove_primary_test_enter_entop(ENTITY *e)
{
pr_test("remove_%s_database(%s_envp%s%s%s);\n", e->name,
the_schema.environment.name,
e->transactional ? ", " : "",
e->transactional ? the_schema.environment.name : "",
e->transactional ? "_txnp" : "");
}
static void
remove_secondary_test_idxop(DB_INDEX *idx)
{
pr_test("remove_%s_index(%s_envp%s%s%s);\n", idx->name,
the_schema.environment.name,
idx->primary->transactional ? ", " : "",
idx->primary->transactional ? the_schema.environment.name : "",
idx->primary->transactional ? "_txnp" : "");
}