Crossfire Server, Branch 1.12
R12190
|
00001 /* 00002 * CrossFire, A Multiplayer game for X-windows 00003 * 00004 * Copyright (C) 2007 Crossfire Development Team 00005 * Copyright (C) 1992 Frank Tore Johansen 00006 * 00007 * This program is free software; you can redistribute it and/or modify 00008 * it under the terms of the GNU General Public License as published by 00009 * the Free Software Foundation; either version 2 of the License, or 00010 * (at your option) any later version. 00011 * 00012 * This program is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 * GNU General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU General Public License 00018 * along with this program; if not, write to the Free Software 00019 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00020 * 00021 * The authors can be reached via e-mail at crossfire-devel@real-time.com 00022 */ 00023 00034 #include <stdlib.h> 00035 #include <check.h> 00036 #include <global.h> 00037 00038 void setup(void) { 00039 /* put any initialisation steps here, they will be run before each testcase */ 00040 } 00041 00042 void teardown(void) { 00043 /* put any cleanup steps here, they will be run after each testcase */ 00044 } 00045 00046 #if 0 00047 static mapstruct *get_random_map(mapstruct *map) { 00048 object *exit_ob; 00049 mapstruct *random; 00050 RMParms rp; 00051 char newmap_name[HUGE_BUF], *cp; 00052 static int reference_number = 0; 00053 int x, y; 00054 00055 exit_ob = NULL; 00056 for (x = 0; x < MAP_WIDTH(map) && exit_ob == NULL; x++) { 00057 for (y = 0; y < MAP_HEIGHT(map) && exit_ob == NULL; y++) { 00058 for (exit_ob = GET_MAP_OB(map, x, y); exit_ob != NULL; exit_ob = exit_ob->above) 00059 if (exit_ob->type == EXIT && exit_ob->msg != NULL) 00060 break; 00061 } 00062 } 00063 00064 if (!exit_ob) 00065 /* this means we reached the end of the random part. */ 00066 return NULL; 00067 00068 /* copied from server/server.c:enter_random_map(). */ 00069 memset(&rp, 0, sizeof(RMParms)); 00070 rp.Xsize = -1; 00071 rp.Ysize = -1; 00072 rp.region = get_region_by_map(exit_ob->map); 00073 if (exit_ob->msg) 00074 set_random_map_variable(&rp, exit_ob->msg); 00075 rp.origin_x = exit_ob->x; 00076 rp.origin_y = exit_ob->y; 00077 strcpy(rp.origin_map, map->path); 00078 00079 /* If we have a final_map, use it as a base name to give some clue 00080 * as where the player is. Otherwise, use the origin map. 00081 * Take the last component (after the last slash) to give 00082 * shorter names without bogus slashes. 00083 */ 00084 if (rp.final_map[0]) { 00085 cp = strrchr(rp.final_map, '/'); 00086 if (!cp) 00087 cp = rp.final_map; 00088 } else { 00089 char buf[HUGE_BUF]; 00090 00091 cp = strrchr(rp.origin_map, '/'); 00092 if (!cp) 00093 cp = rp.origin_map; 00094 /* Need to strip of any trailing digits, if it has them */ 00095 snprintf(buf, sizeof(buf), "%s", cp); 00096 while (isdigit(buf[strlen(buf)-1])) 00097 buf[strlen(buf)-1] = 0; 00098 cp = buf; 00099 } 00100 snprintf(newmap_name, sizeof(newmap_name), "/random/%s%04d", cp+1, reference_number++); 00101 /* now to generate the actual map. */ 00102 return generate_random_map(newmap_name, &rp, NULL); 00103 } 00104 00105 static void do_run() { 00106 mapstruct *worldmap; 00107 mapstruct *random; 00108 mapstruct *old; 00109 int iteration, x, y, map; 00110 object *check; 00111 char path[150]; 00112 00113 for (map = 1; map <= 3; map++) { 00114 snprintf(path, sizeof(path), "/whalingoutpost/underwaterdungeon/level%d", map); 00115 worldmap = ready_map_name(path, 0); 00116 fail_unless(worldmap != NULL, "Can't load %s", path); 00117 00118 random = worldmap; 00119 old = NULL; 00120 iteration = 0; 00121 while (random != NULL) { 00122 random = get_random_map(random); 00123 if (!random) 00124 break; 00125 if (old) 00126 delete_map(old); 00127 old = random; 00128 iteration++; 00129 for (x = 0; x < MAP_WIDTH(random); x++) { 00130 for (y = 0; y < MAP_HEIGHT(random); y++) { 00131 for (check = GET_MAP_OB(random, x, y); check; check = check->above) { 00132 if (check->type == HORN && check->title && strcmp(check->title, "of Plenty") == 0) 00133 fail_unless(check->inv != NULL, "Horn has empty inventory!"); 00134 } 00135 } 00136 } 00137 } 00138 fail_unless(iteration != 0, "did %d iterations", iteration); 00139 if (old) 00140 delete_map(old); 00141 } 00142 } 00143 #endif 00144 00145 #if 0 00146 static void do_run() { 00147 mapstruct *map, *overlay; 00148 int x, y, found = 0, test = 0; 00149 object *check; 00150 00151 overlay = ready_map_name("../../rsc/bug_1727944_unique", MAP_PLAYER_UNIQUE); 00152 fail_unless(overlay != NULL, "Couldn't load unique map ../../rsc/bug_1727944_unique"); 00153 00154 while (found == 0 && test < 10) { 00155 map = ready_map_name("../../rsc/bug_1727944", MAP_PLAYER_UNIQUE); 00156 fail_unless(map != NULL, "couldn't load map ../../rsc/bug_1727944"); 00157 00158 for (x = 0; x < MAP_WIDTH(map); x++) { 00159 for (y = 0; y < MAP_HEIGHT(map); y++) { 00160 for (check = GET_MAP_OB(map, x, y); check; check = check->above) { 00161 if (check->type == HORN) { 00162 fail_unless(check->inv != NULL, "Horn has empty inventory!"); 00163 fail_unless(check->inv->below == NULL, "Horn has 2 items in inventory!"); 00164 if (check->title && strcmp(check->title, "of Plenty") == 0) { 00165 remove_ob(check); 00166 insert_ob_in_map_at(check, overlay, NULL, 0, 2, 3); 00167 found++; 00168 break; 00169 } 00170 } 00171 } 00172 } 00173 } 00174 delete_map(map); 00175 test++; 00176 } 00177 save_map(overlay, SAVE_MODE_OVERLAY); 00178 delete_map(overlay); 00179 } 00180 #endif 00181 00182 extern int artifact_init; 00183 extern int arch_init; 00184 00185 /* Copied from loader.l */ 00186 extern const char *const spell_mapping[]; 00187 00188 static void local_check_loaded_object(object *op) { 00189 int ip; 00190 00191 if (artifact_init) 00192 /* Artifacts are special beasts, let's not check them. */ 00193 return; 00194 00195 /* We do some specialized handling to handle legacy cases of name_pl. 00196 * If the object doesn't have a name_pl, we just use the object name - 00197 * this isn't perfect (things won't be properly pluralized), but works to 00198 * that degree (5 heart is still quite understandable). But the case we 00199 * also have to catch is if this object is not using the normal name for 00200 * the object. In that case, we also want to use the loaded name. 00201 * Otherwise, what happens is that the the plural name will lose 00202 * information (appear as just 'hearts' and not 'goblins heart') 00203 */ 00204 if (op->arch && op->name != op->arch->clone.name && op->name_pl == op->arch->clone.name_pl) { 00205 if (op->name_pl) 00206 free_string(op->name_pl); 00207 op->name_pl = NULL; 00208 } 00209 if (!op->name_pl && op->name) 00210 op->name_pl = add_string(op->name); 00211 00212 /* objects now have a materialname. try to patch it in */ 00213 if (!(IS_WEAPON(op) && op->level > 0)) { 00214 if (op->map != NULL) 00215 set_materialname(op, op->map->difficulty, NULL); 00216 else 00217 set_materialname(op, 5, NULL); 00218 } 00219 /* only do these when program is first run - a bit 00220 * excessive to do this at every run - most of this is 00221 * really just to catch any errors - program will still run, but 00222 * not in the ideal fashion. 00223 */ 00224 if ((op->type == WEAPON || op->type == BOW) && arch_init) { 00225 if (!op->skill) { 00226 LOG(llevError, "Weapon %s lacks a skill.\n", op->name); 00227 } else if ((!strcmp(op->skill, "one handed weapons") && op->body_info[1] != -1) 00228 || (!strcmp(op->skill, "two handed weapons") && op->body_info[1] != -2)) { 00229 LOG(llevError, "weapon %s arm usage does not match skill: %d, %s\n", 00230 op->name, op->body_info[1], op->skill); 00231 } 00232 } 00233 00234 /* We changed last_heal to gen_sp_armour, which is what it 00235 * really does for many objects. Need to catch any in maps 00236 * that may have an old value. 00237 */ 00238 if ((op->type == WEAPON) 00239 || (op->type == ARMOUR) 00240 || (op->type == HELMET) 00241 || (op->type == SHIELD) 00242 || (op->type == RING) 00243 || (op->type == BOOTS) 00244 || (op->type == GLOVES) 00245 || (op->type == AMULET) 00246 || (op->type == GIRDLE) 00247 || (op->type == BRACERS) 00248 || (op->type == CLOAK)) { 00249 if (op->last_heal) { 00250 LOG(llevDebug, "Object %s still has last_heal set, not gen_sp_armour\n", op->name ? op->name : "NULL"); 00251 op->gen_sp_armour = op->last_heal; 00252 op->last_heal = 0; 00253 } 00254 ip = calc_item_power(op, 0); 00255 /* Legacy objects from before item power was in the game */ 00256 if (!op->item_power && ip) { 00257 if (ip > 3) { 00258 LOG(llevDebug, "Object %s had no item power, using %d\n", op->name ? op->name : "NULL", ip); 00259 } 00260 op->item_power = ip; 00261 } 00262 /* Check for possibly bogus values. Has to meet both these criteria - 00263 * something that has item_power 1 is probably just fine if our calculated 00264 * value is 1 or 2 - these values are small enough that hard to be precise. 00265 * similarly, it item_power is 0, the first check will always pass, 00266 * but not the second one. 00267 */ 00268 if (ip > 2*op->item_power && ip > (op->item_power+3)) { 00269 LOG(llevDebug, "Object %s seems to have too low item power? %d > %d\n", op->name ? op->name : "NULL", ip, op->item_power); 00270 } 00271 } 00272 /* Old spellcasting object - need to load in the appropiate object */ 00273 if ((op->type == ROD || op->type == WAND || op->type == SCROLL || op->type == HORN || op->type == FIREWALL || /* POTIONS and ALTARS don't always cast spells, but if they do, update them */ ((op->type == POTION || op->type == ALTAR) && op->stats.sp)) 00274 && !op->inv 00275 && !arch_init) { 00276 object *tmp; 00277 00278 /* Fireall is bizarre in that spell type was stored in dam. Rest are 'normal' 00279 * in that spell was stored in sp. 00280 */ 00281 tmp = create_archetype(spell_mapping[op->type == FIREWALL ? op->stats.dam : op->stats.sp]); 00282 insert_ob_in_ob(tmp, op); 00283 op->randomitems = NULL; /* So another spell isn't created for this object */ 00284 } 00285 00286 /* spellbooks & runes use slaying. But not to arch name, but to spell name */ 00287 if ((op->type == SPELLBOOK || op->type == RUNE) && op->slaying && !op->inv && !arch_init) { 00288 object *tmp; 00289 00290 tmp = create_archetype_by_object_name(op->slaying); 00291 insert_ob_in_ob(tmp, op); 00292 op->randomitems = NULL; /* So another spell isn't created for this object */ 00293 /* without this, value is all screwed up */ 00294 op->value = op->arch->clone.value*op->inv->value; 00295 } 00296 00297 if (QUERY_FLAG(op, FLAG_MONSTER)) { 00298 if (op->stats.hp > op->stats.maxhp) 00299 LOG(llevDebug, "Monster %s has hp set higher than maxhp (%d>%d)\n", op->name, op->stats.hp, op->stats.maxhp); 00300 } 00301 if ((QUERY_FLAG(op, FLAG_GENERATOR) && QUERY_FLAG(op, FLAG_CONTENT_ON_GEN)) 00302 || op->type == CREATOR 00303 || op->type == CONVERTER) { 00304 /* Object will duplicate it's content as part of the 00305 * generation process. To do this, we must flag inventory 00306 * so it remains unevaluated concerning the randomitems and 00307 * the living (a demonlord shouldn't cast from inside generator!) 00308 */ 00309 flag_inv(op, FLAG_IS_A_TEMPLATE); 00310 } 00311 00312 /* Here we'll handle custom monsters. In order to handle them correctly, especially in the fix_object 00313 * method, we'll create a new temporary archetype containing defined values. 00314 * Of course this doesn't apply when loading archetypes or artifacts. 00315 */ 00316 if (arch_init == 0 && artifact_init == 0 && QUERY_FLAG(op, FLAG_MONSTER) && op->arch && !can_merge(op, &op->arch->clone)) { 00317 archetype *temp = get_archetype_struct(); 00318 00319 temp->reference_count++; 00320 temp->name = add_string(op->arch->name); 00321 temp->tail_x = op->arch->tail_x; 00322 temp->tail_y = op->arch->tail_y; 00323 copy_object(op, &temp->clone); 00324 temp->clone.inv = NULL; 00325 temp->clone.env = NULL; 00326 temp->clone.x = 0; 00327 temp->clone.y = 0; 00328 temp->clone.map = NULL; 00329 if (FABS(temp->clone.speed) > MIN_ACTIVE_SPEED) { 00330 /* Clone has a speed, so need to clear that because it isn't on a map. 00331 * But we need to keep the value, because otherwise the customized object 00332 * will have no speed (fix_player() will use the 0 value). So set it 00333 * to zero, call update_ob_speed() to remove it from active list, then 00334 * set its speed back to the original. 00335 */ 00336 temp->clone.speed = 0; 00337 update_ob_speed(&temp->clone); 00338 temp->clone.speed = op->speed; 00339 } 00340 00341 temp->more = op->arch->more; 00342 op->arch = temp; 00343 /* LOG(llevDebug, "created temporary archetype for %s at %d,%d\n", op->name, op->x, op->y); */ 00344 } 00345 } 00346 00347 START_TEST(test_randommaps) { 00348 #if 0 00349 int test; 00350 mapstruct *overlay; 00351 object *check; 00352 00353 for (test = 0; test < 50; test++) 00354 do_run(); 00355 00356 for (test = 0; test < 50; test++) { 00357 overlay = ready_map_name("../../rsc/bug_1727944_unique", MAP_PLAYER_UNIQUE); 00358 fail_unless(overlay != NULL, "Couldn't load unique map ../../rsc/bug_1727944_unique"); 00359 fail_unless(GET_MAP_OB(overlay, 2, 3) != NULL, "No item on spot 2,3?"); 00360 00361 for (check = GET_MAP_OB(overlay, 2, 3)->above; check != NULL; check = check->above) { 00362 fail_unless(check->type == HORN, "Found a non horn?"); 00363 fail_unless(check->inv != NULL, "Horn without a spell!"); 00364 fail_unless(check->inv->below == NULL, "Horn with 2 items in inventory."); 00365 } 00366 save_map(overlay, SAVE_MODE_OVERLAY); 00367 delete_map(overlay); 00368 } 00369 #endif 00370 00371 #if 0 00372 int test; 00373 archetype *horn = find_archetype("horn"); 00374 fail_unless(horn != NULL, "couldn't find archetype horn."); 00375 archetype *horn2 = find_archetype("horn2"); 00376 fail_unless(horn2 != NULL, "couldn't find archetype horn2."); 00377 00378 for (test = 0; test < 100000; test++) { 00379 object *check = arch_to_object(RANDOM()%2 ? horn : horn2); 00380 00381 generate_artifact(check, RANDOM()%100); 00382 fail_unless(check->inv != NULL, "horn without inventory!"); 00383 } 00384 #endif 00385 00386 int test, level, found = 0; 00387 object *the_chest, *check; 00388 mapstruct *map; 00389 treasurelist *tlist = find_treasurelist("uncommon_items"); 00390 fail_unless(tlist != NULL, "couldn't find treasure list uncommon_items"); 00391 00392 for (test = 0; test < 10; test++) { 00393 for (level = 1; level < 120; level++) { 00394 map = get_empty_map(1, 1); 00395 fail_unless(map != NULL, "failed to get empty map"); 00396 map->difficulty = level; 00397 00398 the_chest = create_archetype("chest"); /* was "chest_2" */ 00399 fail_unless(the_chest != NULL, "failed to get chest"); 00400 the_chest->randomitems = tlist; 00401 the_chest->stats.hp = RANDOM()%100; 00402 insert_ob_in_map_at(the_chest, map, NULL, 0, 0, 0); 00403 fix_auto_apply(map); 00404 the_chest = GET_MAP_OB(map, 0, 0); 00405 fail_unless(the_chest != NULL, "failed to recover chest?"); 00406 for (check = the_chest->inv; check; check = check->below) { 00407 if (check->type != HORN) 00408 continue; 00409 local_check_loaded_object(check); 00410 fail_unless(check->inv != NULL, "horn without inventory"); 00411 fail_unless(check->inv->below == NULL, "horn with 2 items"); 00412 fail_unless(check->randomitems == NULL, "horn with randomitems set"); 00413 found++; 00414 } 00415 delete_map(map); 00416 } 00417 } 00418 fail_unless(found > 100, "didn't find 100 horn but %d??", found); 00419 00420 } 00421 END_TEST 00422 00423 Suite *bug_suite(void) { 00424 Suite *s = suite_create("bug"); 00425 TCase *tc_core = tcase_create("Core"); 00426 00427 /*setup and teardown will be called before each test in testcase 'tc_core' */ 00428 tcase_add_checked_fixture(tc_core, setup, teardown); 00429 00430 suite_add_tcase(s, tc_core); 00431 tcase_add_test(tc_core, test_randommaps); 00432 tcase_set_timeout(tc_core, 0); 00433 00434 return s; 00435 } 00436 00437 int main(void) { 00438 int nf; 00439 Suite *s = bug_suite(); 00440 SRunner *sr = srunner_create(s); 00441 00442 srunner_set_fork_status(sr, CK_NOFORK); 00443 init(0, NULL); 00444 00445 srunner_set_xml(sr, LOGDIR "/bugs/bugtrack/1727944.xml"); 00446 srunner_set_log(sr, LOGDIR "/bugs/bugtrack/1727944.out"); 00447 srunner_run_all(sr, CK_ENV); /*verbosity from env variable*/ 00448 nf = srunner_ntests_failed(sr); 00449 srunner_free(sr); 00450 return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 00451 }