1/* 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1996-2009 Oracle. All rights reserved. 5 * 6 */ 7 8/* 9 * This compilation unit contains functions related to the generation 10 * of a simple smoke test for the generated storage layer. 11 */ 12#include "generation.h" 13 14extern int maxbinsz; /* defined in buildpt.c */ 15 16static FILE *test_file; /* stream for generated test code */ 17static int test_indent_level = 0; 18 19static void pr_test(char *, ...); 20static void pr_test_comment(char *, ...); 21static void callback_function_enter_entop(ENTITY *); 22static void callback_function_exit_entop(ENTITY *); 23static void callback_function_attrop(ENTITY *, ATTRIBUTE *, int, int); 24static void declare_record_instances_enter_entop(ENTITY *); 25static void initialize_database_enter_entop(ENTITY *); 26static void initialize_index_enter_entop(DB_INDEX *); 27static void insertion_test_enter_entop(ENTITY *); 28static void insertion_test_exit_entop(ENTITY *); 29static void insertion_test_attrop(ENTITY *, ATTRIBUTE *, int, int); 30static void retrieval_test_enter_entop(ENTITY *); 31static void retrieval_test_exit_entop(ENTITY *); 32static void retrieval_test_attrop(ENTITY *, ATTRIBUTE *, int, int); 33static void invoke_full_iteration_enter_entop(ENTITY *); 34static void invoke_full_iteration_exit_entop(ENTITY *e); 35static void invoke_query_iteration_idxop(DB_INDEX *); 36static void deletion_test_enter_entop(ENTITY *); 37static void deletion_test_exit_entop(ENTITY *e); 38static void close_secondary_test_idxop(DB_INDEX *); 39static void close_primary_test_enter_entop(ENTITY *); 40static void remove_secondary_test_idxop(DB_INDEX *); 41static void remove_primary_test_enter_entop(ENTITY *); 42 43/* 44 * Generate_test is the sole entry point in this module. It 45 * orchestrates the generation of the C code for the smoke test. 46 */ 47void 48generate_test(tfile, hfilename) 49 FILE *tfile; 50 char *hfilename; 51{ 52 test_file = tfile; 53 54 pr_test_comment( 55"Simple test for a Berkeley DB implementation \n\ 56generated from SQL DDL by db_sql \n\ 57"); 58 59 pr_test("\n#include \"%s\"\n\n", hfilename); 60 61 if (maxbinsz != 0) { 62 pr_test_comment("Test data for raw binary types"); 63 pr_test("#define MAXBINSZ %d\n", maxbinsz); 64 pr_test("char binary_data[MAXBINSZ];\n\n"); 65 pr_test_comment("A very simple binary comparison function"); 66 pr_test( 67"char * compare_binary(char *p, int len) \n\ 68{ \n\ 69 if (memcmp(p, binary_data, len) == 0) \n\ 70 return \"*binary values match*\"; \n\ 71 return \"*binary values don't match*\"; \n\ 72} \n\ 73 \n\ 74"); 75 } 76 77 pr_test_comment( 78"These are the iteration callback functions. One is defined per \n\ 79database(table). They are used for both full iterations and for \n\ 80secondary index queries. When a retrieval returns multiple records, \n\ 81as in full iteration over an entire database, one of these functions \n\ 82is called for each record found"); 83 84 iterate_over_entities(&callback_function_enter_entop, 85 &callback_function_exit_entop, 86 &callback_function_attrop); 87 88 pr_test( 89" \n\ 90main(int argc, char **argv) \n\ 91{ \n\ 92 int i; \n\ 93 int ret; \n\ 94 \n\ 95"); 96 test_indent_level++; 97 iterate_over_entities(&declare_record_instances_enter_entop, 98 NULL, 99 NULL); 100 101 iterate_over_entities(&initialize_database_enter_entop, 102 NULL, 103 NULL); 104 105 iterate_over_indexes(&initialize_index_enter_entop); 106 107 pr_test("\n"); 108 109 if (maxbinsz != 0) { 110 pr_test_comment( 111 "Fill the binary test data with random values"); 112 pr_test( 113"for (i = 0; i < MAXBINSZ; i++) binary_data[i] = rand();\n\n"); 114 } 115 116 pr_test_comment( 117"Use the convenience method to initialize the environment. \n\ 118The initializations for each entity and environment can be \n\ 119done discretely if you prefer, but this is the easy way."); 120 pr_test( 121"ret = initialize_%s_environment(); \n\ 122if (ret != 0){ \n\ 123printf(\"Initialize error\"); \n\ 124return ret; \n\ 125} \n\ 126 \n\ 127", 128 the_schema.environment.name); 129 130 pr_test_comment( 131"Now that everything is initialized, insert a single \n\ 132record into each database, using the ...insert_fields \n\ 133functions. These functions take each field of the \n\ 134record as a separate argument"); 135 136 iterate_over_entities(&insertion_test_enter_entop, 137 &insertion_test_exit_entop, 138 &insertion_test_attrop); 139 140 pr_test("\n"); 141 pr_test_comment( 142"Next, retrieve the records just inserted, looking them up \n\ 143by their key values"); 144 145 iterate_over_entities(&retrieval_test_enter_entop, 146 &retrieval_test_exit_entop, 147 &retrieval_test_attrop); 148 149 pr_test("\n"); 150 pr_test_comment( 151"Now try iterating over every record, using the ...full_iteration \n\ 152functions for each database. For each record found, the \n\ 153appropriate ...iteration_callback_test function will be invoked \n\ 154(these are defined above)."); 155 156 iterate_over_entities(&invoke_full_iteration_enter_entop, 157 &invoke_full_iteration_exit_entop, 158 NULL); 159 160 pr_test("\n"); 161 pr_test_comment( 162"For the secondary indexes, query for the known keys. This also \n\ 163results in the ...iteration_callback_test function's being called \n\ 164for each record found."); 165 166 iterate_over_indexes(&invoke_query_iteration_idxop); 167 168 pr_test("\n"); 169 pr_test_comment( 170"Now delete a record from each database using its primary key."); 171 172 iterate_over_entities(&deletion_test_enter_entop, 173 &deletion_test_exit_entop, 174 NULL); 175 176 pr_test("\n"); 177 test_indent_level--; 178 pr_test("exit_error:\n"); 179 test_indent_level++; 180 181 pr_test_comment("Close the secondary index databases"); 182 iterate_over_indexes(&close_secondary_test_idxop); 183 184 pr_test("\n"); 185 pr_test_comment("Close the primary databases"); 186 iterate_over_entities(&close_primary_test_enter_entop, 187 NULL, 188 NULL); 189 190 pr_test("\n"); 191 pr_test_comment("Delete the secondary index databases"); 192 iterate_over_indexes(&remove_secondary_test_idxop); 193 194 pr_test("\n"); 195 pr_test_comment("Delete the primary databases"); 196 iterate_over_entities(&remove_primary_test_enter_entop, 197 NULL, 198 NULL); 199 200 pr_test("\n"); 201 pr_test_comment("Finally, close the environment"); 202 pr_test("%s_envp->close(%s_envp, 0);\n", 203 the_schema.environment.name, the_schema.environment.name); 204 205 pr_test("return ret;\n"); 206 test_indent_level--; 207 pr_test("}\n"); 208} 209 210/* 211 * Emit a printf-formatted string into the test code file. 212 */ 213static void 214pr_test(char *fmt, ...) 215{ 216 va_list ap; 217 char *s; 218 static int enable_indent = 1; 219 220 s = prepare_string(fmt, 221 enable_indent ? test_indent_level : 0, 222 0); 223 224 va_start(ap, fmt); 225 vfprintf(test_file, s, ap); 226 va_end(ap); 227 228 /* 229 * If the last char emitted was a newline, enable indentation 230 * for the next time. 231 */ 232 if (s[strlen(s) - 1] == '\n') 233 enable_indent = 1; 234 else 235 enable_indent = 0; 236} 237 238/* 239 * Emit a formatted comment into the test file. 240 */ 241static void 242pr_test_comment(char *fmt, ...) 243{ 244 va_list ap; 245 char *s; 246 247 s = prepare_string(fmt, test_indent_level, 1); 248 249 va_start(ap, fmt); 250 vfprintf(test_file, s, ap); 251 va_end(ap); 252} 253 254/* 255 * Return the appropriate printf format string for the given type. 256 */ 257static char * 258format_string_for_type(ATTR_TYPE *t) 259{ 260 char *c_type = t->c_type; 261 262 if (is_array(t)) { 263 return ("%s"); 264 } else if (strcmp(c_type, "char") == 0 || 265 strcmp(c_type, "short") == 0 || 266 strcmp(c_type, "int") == 0) { 267 return("%d"); 268 } else if (strcmp(c_type, "long") == 0) { 269 return("%ld"); 270 } else if (strcmp(c_type, "float") == 0) { 271 return("%f"); 272 } else if (strcmp(c_type, "double") == 0) { 273 return("%lf"); 274 } else { 275 fprintf(stderr, 276 "Unexpected C type in schema: %s", c_type); 277 assert(0); 278 } 279 return NULL; /*NOTREACHED*/ 280} 281 282/* 283 * Return a data literal appropriate for the given type, to use as test data. 284 */ 285static char * 286data_value_for_type(ATTR_TYPE *t) 287{ 288 char *c_type = t->c_type; 289 290 if (is_string(t)) { 291 /* If the field is really short, use a tiny string */ 292 if (t->array_dim < 12) 293 return("\"n\""); 294 return("\"ninety-nine\""); 295 } else if (is_array(t)) { 296 return("binary_data"); 297 } else if (strcmp(c_type, "char") == 0 || 298 strcmp(c_type, "short") == 0 || 299 strcmp(c_type, "int") == 0 || 300 strcmp(c_type, "long") == 0) { 301 return("99"); 302 } else if (strcmp(c_type, "float") == 0 || 303 strcmp(c_type, "double") == 0) { 304 return("99.5"); 305 } else { 306 fprintf(stderr, 307 "Unexpected C type in schema: %s", c_type); 308 assert(0); 309 } 310 return NULL; /*NOTREACHED*/ 311} 312 313/* 314 * This entity operation function is called by the attribute iterator 315 * when producing test code that declares record instances. 316 */ 317static void 318declare_record_instances_enter_entop(ENTITY *e) 319{ 320 pr_test("%s_data %s_record;\n", e->name, e->name); 321} 322 323/* 324 * This entity operation function is called by the attribute iterator 325 * when producing test code that initialized database handle. 326 */ 327static void 328initialize_database_enter_entop(ENTITY *e) 329{ 330 pr_test("%s_dbp = NULL;\n", e->name); 331} 332 333/* 334 * This entity operation function is called by the attribute iterator 335 * when producing test code that initialized index handle. 336 */ 337static void 338initialize_index_enter_entop(DB_INDEX *idx) 339{ 340 pr_test("%s_dbp = NULL;\n", idx->name); 341} 342 343static void 344invoke_full_iteration_enter_entop(ENTITY *e) 345{ 346 pr_test( 347"ret = %s_full_iteration(%s_dbp, &%s_iteration_callback_test, \n\ 348 \"retrieval of %s record through full iteration\");\n", 349 e->name, e->name, e->name, e->name); 350} 351static void 352invoke_full_iteration_exit_entop(ENTITY *e) 353{ 354 COMPQUIET(e, NULL); 355 pr_test( 356"if (ret != 0){ \n\ 357 printf(\"Full Iteration Error\\n\"); \n\ 358 goto exit_error; \n\ 359} \n\ 360 \n\ 361"); 362} 363 364/* 365 * This index operation function is called by the attribute iterator 366 * when producing test code that creates the secondary databases. 367 */ 368static void 369invoke_query_iteration_idxop(DB_INDEX *idx) 370{ 371 ATTR_TYPE *key_type = idx->attribute->type; 372 373 pr_test("%s_query_iteration(%s_dbp, ", 374 idx->name, idx->name); 375 376 pr_test("%s", data_value_for_type(key_type)); 377 378 pr_test( 379",\n &%s_iteration_callback_test, \n\ 380 \"retrieval of %s record through %s query\");\n", 381 idx->primary->name, idx->primary->name, idx->name); 382} 383 384/* 385 * This next group of entity and attribute operation functions are 386 * called by the attribute iterator when generating insertion test code. 387 */ 388static void 389insertion_test_enter_entop(ENTITY *e) 390{ 391 pr_test("ret = %s_insert_fields( %s_dbp, ", e->name, e->name); 392} 393static void 394insertion_test_exit_entop(ENTITY *e) 395{ 396 COMPQUIET(e, NULL); 397 pr_test(");\n"); 398 pr_test( 399"if (ret != 0){ \n\ 400 printf(\"Insert error\\n\"); \n\ 401 goto exit_error; \n\ 402} \n\ 403 \n\ 404"); 405} 406static void 407insertion_test_attrop(ENTITY *e, ATTRIBUTE *a, int first, int last) 408{ 409 COMPQUIET(e, NULL); 410 COMPQUIET(first, 0); 411 412 pr_test("%s", data_value_for_type(a->type)); 413 if (!last) 414 pr_test(", "); 415} 416 417/* 418 * This next group of index and attribute operation functions are 419 * called by the attribute iterator when generating the iteration 420 * callback function. 421 */ 422static void 423callback_function_attrop(ENTITY *e, ATTRIBUTE *a, int first, int last) 424{ 425 COMPQUIET(first, 0); 426 COMPQUIET(last, 0); 427 428 pr_test("printf(\"%s->%s: ", e->name, a->name); 429 430 pr_test("%s", format_string_for_type(a->type)); 431 432 if (is_array(a->type) && !is_string(a->type)) { 433 pr_test("\\n\", compare_binary(%s_record->%s, %s));\n", 434 e->name, a->name, array_dim_name(e, a) ); 435 } else { 436 pr_test("\\n\", %s_record->%s);\n", e->name, a->name); 437 } 438} 439static void 440callback_function_enter_entop(ENTITY *e) 441{ 442 pr_test( 443" \n\ 444void %s_iteration_callback_test(void *msg, %s_data *%s_record) \n\ 445{ \n\ 446 printf(\"In iteration callback, message is: %%s\\n\", (char *)msg);\n\n", 447 e->name, e->name, e->name); 448 449 test_indent_level++; 450} 451static void 452callback_function_exit_entop(ENTITY *e) 453{ 454 COMPQUIET(e, NULL); 455 456 test_indent_level--; 457 pr_test("}\n\n"); 458} 459 460/* 461 * This next group of entity and attribute operation functions are 462 * called by the attribute iterator when generating retrieval test code 463 */ 464static void 465retrieval_test_enter_entop(ENTITY *e) 466{ 467 pr_test("\nprintf(\"Retrieval of %s record by key\\n\");\n", e->name); 468 pr_test("ret = get_%s_data( %s_dbp, %s, &%s_record);\n\n", 469 e->name, e->name, 470 data_value_for_type(e->primary_key->type), e->name); 471} 472static void 473retrieval_test_exit_entop(ENTITY *e) 474{ 475 COMPQUIET(e, NULL); 476 pr_test( 477"if (ret != 0) \n\ 478{ \n\ 479 printf(\"Retrieve error\\n\"); \n\ 480 goto exit_error; \n\ 481} \n\ 482 \n\ 483"); 484} 485 486static void 487retrieval_test_attrop(ENTITY *e, ATTRIBUTE *a, int first, int last) 488{ 489 COMPQUIET(first, 0); 490 COMPQUIET(last, 0); 491 492 pr_test("printf(\"%s.%s: ", e->name, a->name); 493 494 pr_test("%s", format_string_for_type(a->type)); 495 496 if (is_array(a->type) && !is_string(a->type)) { 497 pr_test("\\n\", compare_binary(%s_record.%s, %s));\n", 498 e->name, a->name, array_dim_name(e, a) ); 499 } else { 500 pr_test("\\n\", %s_record.%s);\n", e->name, a->name); 501 } 502} 503 504/* 505 * This entity operation function is 506 * called by the attribute iterator when generating deletion test code. 507 */ 508static void 509deletion_test_enter_entop(ENTITY *e) 510{ 511 pr_test("ret = delete_%s_key( %s_dbp, %s);\n", 512 e->name, e->name, data_value_for_type(e->primary_key->type)); 513} 514static void 515deletion_test_exit_entop(ENTITY *e) 516{ 517 COMPQUIET(e, NULL); 518 pr_test( 519"if (ret != 0) { \n\ 520 printf(\"Delete error\\n\"); \n\ 521 goto exit_error; \n\ 522} \n\ 523 \n\ 524"); 525} 526 527/* This entity operation function generates primary database closures. */ 528static void 529close_primary_test_enter_entop(ENTITY *e) 530{ 531 pr_test( 532"if (%s_dbp != NULL) \n\ 533 %s_dbp->close(%s_dbp, 0); \n\ 534 \n\ 535", 536 e->name, e->name, e->name); 537} 538 539/* This entity operation function generates secondary database closures. */ 540static void 541close_secondary_test_idxop(DB_INDEX *idx) 542{ 543 pr_test( 544"if (%s_dbp != NULL) \n\ 545 %s_dbp->close(%s_dbp, 0); \n\ 546 \n\ 547", 548 idx->name, idx->name, idx->name); 549} 550 551/* This entity operation function generates primary database closures. */ 552static void 553remove_primary_test_enter_entop(ENTITY *e) 554{ 555 pr_test("remove_%s_database(%s_envp);\n", e->name, 556 the_schema.environment.name); 557} 558 559/* This entity operation function generates secondary database closures. */ 560static void 561remove_secondary_test_idxop(DB_INDEX *idx) 562{ 563 pr_test("remove_%s_index(%s_envp);\n", idx->name, 564 the_schema.environment.name); 565} 566