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