00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include <basis/chaos.h>
00020 #include <basis/guards.h>
00021 #include <basis/istring.h>
00022 #include <data_struct/int_hash.cpp>
00023 #include <mechanisms/time_stamp.h>
00024 #include <opsystem/application_shell.h>
00025 #include <loggers/console_logger.h>
00026 #include <loggers/file_logger.h>
00027 #include <data_struct/static_memory_gremlin.h>
00028 #include <textual/string_manipulation.h>
00029
00030 const int TEST_DURATION = 4 * MINUTE_ms;
00031
00032
00033 const int MAX_DEFAULT_BITS = 2;
00034
00035
00037
00038 enum test_actions {
00039 FIRST_TEST = 38,
00040 ADD = FIRST_TEST,
00041
00042 ADD_ADD,
00043
00044
00045 ZAP,
00046
00047 ADD_ZAP,
00048
00049 ZAP_ADD,
00050
00051
00052 FIND,
00053
00054 ACQUIRE,
00055
00056 FIND_ZAP_ADD,
00057
00058
00059 ACQUIRE_ADD_ZAP,
00060
00061
00062 FIND_ADD_FIND,
00063
00064
00065
00066 RESET,
00067
00068 CHECK_SANITY,
00069
00070
00071 REHASH,
00072
00073 COPY,
00074
00075 LAST_TEST = COPY
00076 };
00077
00079
00080
00081
00082 class data_shuttle
00083 {
00084 public:
00085 int food_bar;
00086 istring snacky_string;
00087 bool hungry;
00088 byte_array chunk;
00089 chaos chao;
00090
00091 data_shuttle()
00092 : snacky_string(string_manipulation::make_random_name()),
00093 chunk(chao.inclusive(100, 10000)) {}
00094 };
00095
00097
00098 class test_int_hash : public application_shell
00099 {
00100 public:
00101 test_int_hash();
00102
00103 int execute();
00104
00105
00106 bool perform_a_test(test_actions test_type);
00107
00108
00109 bool pick_a_test();
00110
00111
00112 IMPLEMENT_CLASS_NAME("test_int_hash");
00113
00114 static bool equivalence_applier(const int &key, data_shuttle &item,
00115 void *dlink);
00116
00117 static const char *test_name(test_actions test_type);
00118
00119
00120 bool test_add();
00121 bool test_add_add();
00122 bool test_zap();
00123 bool test_add_zap();
00124 bool test_zap_add();
00125 bool test_find();
00126 bool test_acquire();
00127 bool test_find_zap_add();
00128 bool test_acquire_add_zap();
00129 bool test_find_add_find();
00130 bool test_reset();
00131 bool test_check_sanity();
00132 bool test_copy();
00133 bool test_rehash();
00134
00135 private:
00136 int_set _keys_in_use;
00137 int_hash<data_shuttle> _the_table;
00138 int _hits[LAST_TEST - FIRST_TEST + 1];
00139 int _tested;
00140 };
00141
00143
00144 typedef int_hash<data_shuttle> our_hash;
00145
00147
00148 test_int_hash::test_int_hash()
00149 : application_shell("t_hash_table"),
00150 _the_table(MAX_DEFAULT_BITS),
00151 _tested(0)
00152 {
00153 for (int i = FIRST_TEST; i <= LAST_TEST; i++)
00154 _hits[i - FIRST_TEST] = 0;
00155 }
00156
00157 int test_int_hash::execute()
00158 {
00159 time_stamp exit_time(TEST_DURATION);
00160 while (time_stamp() < exit_time) pick_a_test();
00161 log(isprintf("did %d tests.\n", _tested));
00162 log("Test Activity:");
00163 for (int i = 0; i < LAST_TEST - FIRST_TEST + 1; i++)
00164 log(istring(istring::SPRINTF, "%d (%s): %d hits", i + FIRST_TEST,
00165 test_name(test_actions(i + FIRST_TEST)), _hits[i]));
00166 log(isprintf("note that test %d will seldom be executed.", RESET));
00167 guards::alert_message("hash_table:: works for those functions tested.");
00168 return 0;
00169 }
00170
00171 const char *test_int_hash::test_name(test_actions test_type)
00172 {
00173 switch (test_type) {
00174 case ADD: return "ADD";
00175 case ADD_ADD: return "ADD_ADD";
00176 case ZAP: return "ZAP";
00177 case ADD_ZAP: return "ADD_ZAP";
00178 case ZAP_ADD: return "ZAP_ADD";
00179 case FIND: return "FIND";
00180 case ACQUIRE: return "ACQUIRE";
00181 case FIND_ZAP_ADD: return "FIND_ZAP_ADD";
00182 case ACQUIRE_ADD_ZAP: return "ACQUIRE_ADD_ZAP";
00183 case FIND_ADD_FIND: return "FIND_ADD_FIND";
00184 case RESET: return "RESET";
00185 case COPY: return "COPY";
00186 case REHASH: return "REHASH";
00187 case CHECK_SANITY: return "CHECK_SANITY";
00188 default: return "UnknownTest";
00189 }
00190 }
00191
00192 bool test_int_hash::perform_a_test(test_actions test_type)
00193 {
00194 FUNCDEF("perform_a_test");
00195
00196
00197
00198 switch (test_type) {
00199 case ADD: return test_add();
00200 case ADD_ADD: return test_add_add();
00201 case ZAP: return test_zap();
00202 case ADD_ZAP: return test_add_zap();
00203 case ZAP_ADD: return test_zap_add();
00204 case FIND: return test_find();
00205 case ACQUIRE: return test_acquire();
00206 case FIND_ZAP_ADD: return test_find_zap_add();
00207 case ACQUIRE_ADD_ZAP: return test_acquire_add_zap();
00208 case FIND_ADD_FIND: return test_find_add_find();
00209 case RESET: return test_reset();
00210 case COPY: return test_copy();
00211 case REHASH: return test_rehash();
00212 case CHECK_SANITY: return test_check_sanity();
00213 default:
00214 deadly_error(class_name(), func, "missing case has been seen!");
00215 return false;
00216 }
00217 }
00218
00219 bool test_int_hash::pick_a_test()
00220 {
00221 _tested++;
00222 return perform_a_test(test_actions(randomizer().inclusive(FIRST_TEST,
00223 LAST_TEST)));
00224 }
00225
00226 bool test_int_hash::test_add()
00227 {
00228 FUNCDEF("test_add");
00229 _hits[ADD - FIRST_TEST]++;
00230 int random_id = randomizer().inclusive(-MAXINT / 4, MAXINT / 4);
00231 data_shuttle *to_add = new data_shuttle;
00232 to_add->snacky_string = string_manipulation::make_random_name();
00233 to_add->food_bar = random_id;
00234 _the_table.add(random_id, to_add);
00235 if (_keys_in_use.member(random_id))
00236 return true;
00237 _keys_in_use.add(random_id);
00238 return true;
00239 }
00240
00242
00243 int_hash<data_shuttle> *_hang_on = NIL;
00244
00245
00246 bool test_int_hash::equivalence_applier(const int &key, data_shuttle &item,
00247 void *formal(dlink))
00248 {
00249 FUNCDEF("equivalence_applier");
00250 data_shuttle *found = _hang_on->find(key);
00251 if (!found)
00252 deadly_error(test_int_hash::static_class_name(), func, "no equivalent entry was found in second list");
00253
00254 if (item.food_bar != found->food_bar)
00255 deadly_error(test_int_hash::static_class_name(), func, "food_bar differs");
00256 if (item.snacky_string != found->snacky_string)
00257 deadly_error(test_int_hash::static_class_name(), func, "snacky_string differs");
00258 if (item.hungry != found->hungry)
00259 deadly_error(test_int_hash::static_class_name(), func, "hungry differs");
00260 return true;
00261 }
00262
00263 bool test_int_hash::test_rehash()
00264 {
00265 FUNCDEF("test_rehash");
00266
00267 int maybe = randomizer().inclusive(1, 50);
00268 if (maybe < 32) return true;
00269
00270 _hits[REHASH - FIRST_TEST]++;
00271
00272 int_hash<data_shuttle> table_copy(_the_table.max_bits());
00273
00274
00275 copy_hash_table(table_copy, _the_table);
00276
00277
00278
00279 _the_table.rehash(randomizer().inclusive(1, 20));
00280
00281
00282
00283 _hang_on = &_the_table;
00284 table_copy.apply(equivalence_applier, NIL);
00285
00286
00287 return true;
00288 }
00289
00290 bool test_int_hash::test_copy()
00291 {
00292 FUNCDEF("test_copy");
00293
00294 int maybe = randomizer().inclusive(1, 50);
00295 if (maybe > 16) return true;
00296
00297 _hits[COPY - FIRST_TEST]++;
00298
00299 int_hash<data_shuttle> table_copy(MAX_DEFAULT_BITS);
00300
00301
00302 copy_hash_table(table_copy, _the_table);
00303
00304
00305
00306 _hang_on = &_the_table;
00307 table_copy.apply(equivalence_applier, NIL);
00308
00309
00310 return true;
00311 }
00312
00314
00315 bool test_int_hash::test_add_add()
00316 {
00317 FUNCDEF("test_add_add");
00318 _hits[ADD_ADD - FIRST_TEST]++;
00319 int random_id = randomizer().inclusive(-MAXINT / 4, MAXINT / 4);
00320 data_shuttle *to_add = new data_shuttle;
00321 to_add->snacky_string = string_manipulation::make_random_name();
00322 to_add->food_bar = random_id;
00323 _the_table.add(random_id, to_add);
00324
00325 if (!_keys_in_use.member(random_id)) _keys_in_use.add(random_id);
00326
00327
00328 data_shuttle *next_add = new data_shuttle;
00329 next_add->snacky_string = string_manipulation::make_random_name();
00330 next_add->food_bar = random_id;
00331 if (_the_table.add(random_id, next_add) != our_hash::EXISTING)
00332 deadly_error(class_name(), func, "second add said first failed!");
00333
00334 return true;
00335 }
00336
00337 bool test_int_hash::test_zap()
00338 {
00339 FUNCDEF("test_zap");
00340 int maybe = randomizer().inclusive(1, 1000);
00341 if (maybe > 50) return true;
00342 if (!_keys_in_use.elements()) return false;
00343 _hits[ZAP - FIRST_TEST]++;
00344 int rand_indy = randomizer().inclusive(0, _keys_in_use.elements() - 1);
00345 int dead_key = _keys_in_use[rand_indy];
00346 _keys_in_use.remove(dead_key);
00347 if (!_the_table.zap(dead_key))
00348 deadly_error(class_name(), func, "key should have been present but wasn't");
00349 return true;
00350 }
00351
00352 bool test_int_hash::test_add_zap()
00353 {
00354 FUNCDEF("test_add_zap");
00355
00356 _hits[ADD_ZAP - FIRST_TEST]++;
00357 int random_id = randomizer().inclusive(-MAXINT / 4, MAXINT / 4);
00358 data_shuttle *to_add = new data_shuttle;
00359 to_add->snacky_string = string_manipulation::make_random_name();
00360 to_add->food_bar = random_id;
00361 _the_table.add(random_id, to_add);
00362
00363 if (!_the_table.zap(random_id))
00364 deadly_error(class_name(), func, "key should have been present but wasn't");
00365 return true;
00366 }
00367
00368 bool test_int_hash::test_zap_add()
00369 {
00370 FUNCDEF("test_zap_add");
00371 if (!_keys_in_use.elements()) return false;
00372 _hits[ZAP_ADD - FIRST_TEST]++;
00373 int rand_indy = randomizer().inclusive(0, _keys_in_use.elements() - 1);
00374
00375 int dead_key = _keys_in_use[rand_indy];
00376 if (!_the_table.zap(dead_key))
00377 deadly_error(class_name(), func, "key should have been there but was not!");
00378
00379 data_shuttle *to_add = new data_shuttle;
00380 to_add->snacky_string = string_manipulation::make_random_name();
00381 to_add->food_bar = dead_key;
00382 outcome ret = _the_table.add(dead_key, to_add);
00383 if (ret != our_hash::IS_NEW)
00384 deadly_error(class_name(), func, "key was already present somehow!");
00385 return true;
00386 }
00387
00388 int functional_return(int to_pass) {
00389 return to_pass;
00390 }
00391
00392 bool test_int_hash::test_find()
00393 {
00394 FUNCDEF("test_find");
00395 if (!_keys_in_use.elements()) return false;
00396 _hits[FIND - FIRST_TEST]++;
00397 int rand_indy = randomizer().inclusive(0, _keys_in_use.elements() - 1);
00398 int find_key = _keys_in_use[rand_indy];
00399 data_shuttle *found = NIL;
00400 if (!_the_table.find(functional_return(find_key), found))
00401 deadly_error(class_name(), func, "key should have been there but was not!");
00402 if (!_the_table.find(functional_return(find_key)))
00403 deadly_error(class_name(), func, "find2: key should have been there but was not!");
00404 if (!found)
00405 deadly_error(class_name(), func, "key was found but contents were NIL!");
00406 if (found->food_bar != find_key)
00407 deadly_error(class_name(), func, "stored key differed from real key!");
00408 if (!found->snacky_string.length())
00409 deadly_error(class_name(), func, "stored string had zero length!");
00410 return true;
00411 }
00412
00413 bool test_int_hash::test_acquire()
00414 {
00415 FUNCDEF("test_acquire");
00416 int maybe = randomizer().inclusive(1, 1000);
00417 if (maybe > 150) return true;
00418 if (!_keys_in_use.elements()) return false;
00419 _hits[ACQUIRE - FIRST_TEST]++;
00420 int rand_indy = randomizer().inclusive(0, _keys_in_use.elements() - 1);
00421 int find_key = _keys_in_use[rand_indy];
00422 _keys_in_use.remove(find_key);
00423 data_shuttle *found = _the_table.acquire(find_key);
00424 if (!found)
00425 deadly_error(class_name(), func, "key should have been there but was not!");
00426 if (found->food_bar != find_key)
00427 deadly_error(class_name(), func, "stored key differed from real key!");
00428 if (!found->snacky_string.length())
00429 deadly_error(class_name(), func, "stored string had zero length!");
00430 WHACK(found);
00431 found = _the_table.acquire(find_key);
00432 if (found)
00433 deadly_error(class_name(), func, "key was supposedly there after zap!");
00434 return true;
00435 }
00436
00437 bool test_int_hash::test_find_zap_add()
00438 {
00439 FUNCDEF("test_find_zap_add");
00440
00441 if (!_keys_in_use.elements()) return false;
00442 _hits[FIND_ZAP_ADD - FIRST_TEST]++;
00443 int rand_indy = randomizer().inclusive(0, _keys_in_use.elements() - 1);
00444
00445 int find_key = _keys_in_use[rand_indy];
00446 data_shuttle *found = NIL;
00447 if (!_the_table.find(find_key, found))
00448 deadly_error(class_name(), func, "key should have been there but was not!");
00449 if (!_the_table.find(find_key))
00450 deadly_error(class_name(), func, "find2: key should have been there but was not!");
00451 if (!found)
00452 deadly_error(class_name(), func, "key was found but contents were NIL!");
00453 if (found->food_bar != find_key)
00454 deadly_error(class_name(), func, "stored key differed from real key!");
00455 if (!found->snacky_string.length())
00456 deadly_error(class_name(), func, "stored string had zero length!");
00457
00458 if (!_the_table.zap(find_key))
00459 deadly_error(class_name(), func, "could not zap the item we had found!");
00460
00461 data_shuttle *to_add = new data_shuttle;
00462 to_add->snacky_string = string_manipulation::make_random_name();
00463 to_add->food_bar = find_key;
00464 if (_the_table.add(find_key, to_add) != our_hash::IS_NEW)
00465 deadly_error(class_name(), func, "the item we zapped was still there!");
00466 return true;
00467 }
00468
00469 bool test_int_hash::test_reset()
00470 {
00471 FUNCDEF("test_reset");
00472 int maybe = randomizer().inclusive(1, 1000);
00473
00474 if ( (maybe > 372) || (maybe < 368) ) return true;
00475
00476
00477 _hits[RESET - FIRST_TEST]++;
00478 _the_table.reset();
00479 for (int i = _keys_in_use.elements() - 1; i >= 0; i--) {
00480 int dead_key = _keys_in_use[i];
00481 if (_the_table.acquire(dead_key))
00482 deadly_error(class_name(), func, "after reset, we found an item!");
00483 _keys_in_use.remove(dead_key);
00484 }
00485 return true;
00486 }
00487
00488
00489
00490 bool test_int_hash::test_acquire_add_zap()
00491 {
00492 _hits[ACQUIRE_ADD_ZAP - FIRST_TEST]++;
00493 return false;
00494 }
00495
00496 bool test_int_hash::test_find_add_find()
00497 {
00498 _hits[FIND_ADD_FIND - FIRST_TEST]++;
00499 return false;
00500 }
00501
00502 bool test_int_hash::test_check_sanity()
00503 {
00504 _hits[CHECK_SANITY - FIRST_TEST]++;
00505 return false;
00506 }
00507
00509
00510 HOOPLE_MAIN(test_int_hash, )
00511