C25ULNTKMNOXT72ERWSFQZQW5E2OIM3TOGAAA33Z7EZQMZHRS2MAC
GIDTM5YI3WHSH6CNS2MIQSQ3YZSNO3SZWNI4FUGSFDORFY6UDUAQC
POACC5YR6QQGEX6GGMUJKGCDQNX6P6FUF4UQTYGOSHFOHJ26X4SQC
R55VDFKFAQM32BY36VJBAAJH2C5WJNNYGAXLAH5VEI4Y6NYRLFDQC
C7WZ2EB2RK7JH5UG34EHFFOLHG5UVJU7BDRJ5J5GCB2ZRIVQGLGAC
Z5PVF4BKRAUD3CTW26IDYI2XV7H2YDKFKBWDQYWUUABCKWHF4KWAC
UL7XFKMUX3WIU4O2LZANK4ECJ654UZPDBFGNXUEYZYOLKBYBCG6AC
KLJIFEBICFQ76DNM3MTS2PJ5LTEIOBIW5QZTNW722WHPKIST5UQQC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
4Q5OYUKF2SGF7WHMIVYFZVXXDCFUCYY534VLOGKWYRSPYKRXMVDAC
FSJKED4U2SOUP64DTHF2NEGAYY7EUMSIDKC2SATEXAXEVOCNL3CAC
QLV7Y2LTDKTVMYFLM7UCNRUXU6PC5535XV6UE27N56AHHIGXEIWAC
SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
MQ62OAMLGJVRW2QIL4PAZRAU6PC52ZVGY2FCOBIY6IWGQIHMU5CAC
Y56C5OMUQ5XF2G6DKDV4R5MED44UOIUPTBBQVWQBUHYIXYA5MOZAC
UBQTNLYGD3SNWMUNGWUPX7EXEGQXOXCFCPWIVWBFE7ID7DJLPFWAC
ZLQAAP55CJ77XIJN3DZVPT4GTTVLIBFJLIJJKI6L5UBSHX7VUK6AC
AUXHSGS4EFOPZ6TVZYWNVOUDO7NYKUKE3HBKGQQWTALSVFOE3HAAC
55PFDYPVE6JVGDYPCFUE4XS2523PVSV4CSIFRW6A2GGX4I6VWRWQC
7IERR3KESLBDHEMGSFRYGA66PZBPGKPUGMFDCSFR3WDX7RXD5OTAC
FKRLQJYHK3GB6HHSNNFQ7L57ZAR52LSTBG5B6SNJBYZ2LQ36P67AC
RN242L3YZK35BFY7JRTCSYWD6FWDCRFUFDI7PKYFUCA2UPX6ZEAQC
4N5PW5S3OV25HFN634NNWMMYX26NA2TB6TVFG4UMYSZ2VBJWKE4QC
X7MFMKQTNZ2IWBFVGS6WQV7NRNKJ3DWQAW2X7IQMFQQXW24AHPZQC
ED62QWGKBPORWVKDFOQRKJXEIWZVNGR3O4KWQBDSRNPT36AYOQYAC
SM6YRPYZS6LMDQA6X3VAOK2PGMUFKPD7JMWJISOQSMX2CBR4ISPAC
SIDH2P7NBIG5KEOE27XHD3ZT2NQ2OJZFN6VZXWNWYFFY5YVXSSVQC
7Y5HSDFKA5TPLS2TWTRFMQVX6UXUDHXU5MUMXQSDFAIY4THQ3BIQC
OFTOEJSRSA2EZOAQOJCWWQNDX7IVVAR565JK3DZCBG54TBHHCIDQC
PKXXBHS3LWLPZI2QVRX22MSQ4R2626IXRSNHFFYHXYTLJJQU54LQC
C55G5JGGSVWMU7XVEJL6YZZLDXQZGRN7JQOAALS6WIKFPX3L2U6QC
ASLW3Z5PAVZSWJEMMMVZT226P44EKSAD47QS72JIFJESAI3RPN3AC
GSQ72ULBSL6WBJZUB3GJKAPQDXZIQV7B2TDBA5OP2WVGHVJMCQFQC
FSD7GIK3YLZXWLEH37BU6KV3IUCFGXPQL6IZ7H65YWNRBEKDBX5AC
W45PMU4HNPSAMMEBJ4XH4MTHLPVIASZT4FXTBPID5LFXKIMNUBKAC
DOZORMA366M4HB5JKSS27BMCR6ET7QNZNND2B7KV3NVEEPR5H7EAC
ZHFUXYUHS6V47WK2NRH7OU6RX77NRKTXOZC3MND2GG7PEEWSGFTAC
25CH7HH4LKXFIZ75YNMXS3TSXO6O27DYSOPLOD45K4OCNFWLS4LQC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
AUXVWXWIFSTWFA6VZXN2FMG7FQEKRZVV6MD32VQQ7J2RKCXHAVGAC
ZFGIHLY6UMKMJOU62DUHJWHLFU76ML226WNDRIKOYHMG2BEL7PNQC
OIAQU4VDTZ3EHBNC7FZIOW2QEQLTDHZ7O46XW2YWM6JRVBC66UPQC
N7J2IWU7B34XIUYWHJF2RTRAXXAQLVVZLWWHFWN4FC5PLNFFEWCAC
CH7JECYYH35H4TRHRNRTRJCQTQLZ2WRH62TKV72SUIU2RTK5OH7AC
NVSFIV2ZKP44XHCSCXG6OZVGL67OIFINC34J2EMKTA4KULCERUEAC
BA6B3ABSAZSAUBZTF2IWRQELSAS4IKFMDVBJZZ5ENHTP7J2Q52QQC
GVDKSZGU5NDWQAADOPCIURSBQG2T7VMFDKAF75Z5T3KVVP6744VQC
-- Vet LOS for symmetry.
local FAILMAP = 'losfail.map'
local checks = 0
local function test_los()
-- Send the player to a random spot on the level.
you.random_teleport()
-- Forcibly redo LOS.
you.losight()
-- And draw the view to keep the watcher entertained.
crawl.redraw_view()
checks = checks + 1
local you_x, you_y = you.pos()
local visible_spots = { }
for y = -8, 8 do
for x = -8, 8 do
if x ~= 0 or y ~= 0 then
local px, py = x + you_x, y + you_y
if you.see_grid(px, py) then
table.insert(visible_spots, { px, py })
end
end
end
end
-- For each position in LOS, jump to that position and make sure we
-- can see the original spot.
for _, spot in ipairs(visible_spots) do
local x, y = unpack(spot)
you.moveto(x, y)
you.losight()
if not you.see_grid(you_x, you_y) then
crawl.redraw_view()
local this_p = dgn.point(x, y)
local you_p = dgn.point(you_x, you_y)
dgn.grid(you_x, you_y, "floor_special")
dgn.dbg_dump_map(FAILMAP)
assert(false,
"LOS asymmetry detected (iter #" .. checks .. "): " .. you_p ..
" sees " .. this_p .. ", but not vice versa." ..
" Map saved to " .. FAILMAP)
end
end
end
local function run_los_tests(depth, nlevels, tests_per_level)
local place = "D:" .. depth
crawl.mpr("Running LOS tests on " .. place)
dgn.dbg_goto_place(place)
for lev_i = 1, nlevels do
dgn.dbg_flush_map_memory()
dgn.dbg_generate_level()
for t_i = 1, tests_per_level do
test_los()
end
end
end
for depth = 1, 27 do
run_los_tests(depth, 10, 100)
end
// WARNING: This is a very low-level call.
LUAFN(dgn_dbg_goto_place)
{
try
{
const level_id id = level_id::parse_level_id(luaL_checkstring(ls, 1));
you.level_type = id.level_type;
if (id.level_type == LEVEL_DUNGEON)
{
you.where_are_you = static_cast<branch_type>(id.branch);
you.your_level = absdungeon_depth(id.branch, id.depth);
}
}
catch (const std::string &err)
{
luaL_error(ls, err.c_str());
}
return (0);
}
LUAFN(dgn_dbg_flush_map_memory)
{
dgn_flush_map_memory();
init_level_connectivity();
return (0);
}
LUAFN(dgn_dbg_generate_level)
{
no_messages mx;
env.show.init(0);
env.map.init(map_cell());
builder(you.your_level, you.level_type);
return (0);
}
// see_grid should not be exposed to user scripts. The game should
// never disclose grid coordinates to the player. Therefore we load it
// only into the core Lua interpreter (dlua), never into the user
// script interpreter (clua).
LUARET1(you_see_grid, boolean,
see_grid(luaL_checkint(ls, 1), luaL_checkint(ls, 2)))
LUARET1(you_see_grid_no_trans, boolean,
see_grid_no_trans(luaL_checkint(ls, 1), luaL_checkint(ls, 2)))
LUAFN(you_moveto)
{
const coord_def place(luaL_checkint(ls, 1), luaL_checkint(ls, 2));
ASSERT(map_bounds(place));
you.moveto(place);
return (0);
}
LUAFN(you_random_teleport)
{
you_teleport_now(false, false);
return (0);
}
LUAFN(you_losight)
{
calc_show_los();
return (0);
}
}
std::vector<std::string> get_dir_files_ext(const std::string &dir,
const std::string &ext)
{
const std::vector<std::string> allfiles(get_dir_files(dir));
std::vector<std::string> filtered;
for (int i = 0, size = allfiles.size(); i < size; ++i)
if (ends_with(allfiles[i], ext))
filtered.push_back(allfiles[i]);
return (filtered);
#ifndef CTEST_H
#define CTEST_H
#include "AppHdr.h"
#ifdef DEBUG_DIAGNOSTICS
namespace crawl_tests
{
void run_tests(bool exit_on_complete);
}
#endif
#endif
/*
* File: ctest.cc
* Summary: Crawl Lua test cases
* Written by: Darshan Shaligram
*
* Modified for Crawl Reference by $Author$ on $Date$
*
* ctest runs Lua tests found in the test directory. The intent here
* is to test parts of Crawl that can be easily tested from within Crawl
* itself (such as LOS). As a side-effect, writing Lua bindings to support
* tests will expand the available Lua bindings. :-)
*
* Tests will run only with Crawl built in its source tree without
* DATA_DIR_PATH set.
*/
#include "AppHdr.h"
#if DEBUG_DIAGNOSTICS
#include "clua.h"
#include "files.h"
#include "luadgn.h"
#include "maps.h"
#include "stuff.h"
#include <algorithm>
#include <vector>
namespace crawl_tests
{
const std::string test_dir = "test";
const std::string test_player_name = "Superbug99";
int ntests = 0;
int nsuccess = 0;
typedef std::pair<std::string, std::string> file_error;
std::vector<file_error> failures;
void reset_test_data()
{
ntests = 0;
nsuccess = 0;
failures.clear();
// XXX: Good grief, you.your_name is still not a C++ string?!
strncpy(you.your_name, test_player_name.c_str(), kNameLen);
you.your_name[kNameLen - 1] = 0;
}
int crawl_begin_test(lua_State *ls)
{
mprf(MSGCH_PROMPT, "Starting tests: %s", luaL_checkstring(ls, 1));
lua_pushnumber(ls, ++ntests);
return (1);
}
int crawl_test_success(lua_State *ls)
{
mprf(MSGCH_PROMPT, "Test success: %s", luaL_checkstring(ls, 1));
lua_pushnumber(ls, ++nsuccess);
return (1);
}
static const struct luaL_reg crawl_test_lib[] =
{
{ "begin_test", crawl_begin_test },
{ "test_success", crawl_test_success },
{ NULL, NULL }
};
void init_test_bindings()
{
lua_stack_cleaner clean(dlua);
luaL_openlib(dlua, "crawl", crawl_test_lib, 0);
}
void run_test(const std::string &file)
{
++ntests;
const std::string path(catpath(test_dir, file));
dlua.execfile(path.c_str(), true, false);
if (dlua.error.empty())
++nsuccess;
else
failures.push_back(file_error(file, dlua.error));
}
// Assumes curses has already been initialized.
bool run_tests(bool exit_on_complete)
{
run_map_preludes();
reset_test_data();
init_test_bindings();
// Get a list of Lua files in test. Order of execution of
// tests should be irrelevant.
const std::vector<std::string> tests(
get_dir_files_ext(test_dir, ".lua"));
std::for_each(tests.begin(), tests.end(),
run_test);
if (exit_on_complete)
{
cio_cleanup();
for (int i = 0, size = failures.size(); i < size; ++i)
{
const file_error &fe(failures[i]);
fprintf(stderr, "Test error: %s\n",
fe.second.c_str());
}
const int code = failures.empty() ? 0 : 1;
end(code, false, "%d tests, %d succeeded, %d failed",
ntests, nsuccess, failures.size());
}
return (failures.empty());
}
}
#endif // DEBUG_DIAGNOSTICS