raze/polymer/eduke32/source/lunatic/test.elua
helixhorned 4ed1eaa520 Lunatic: accept any number of *.lua modules on the command line.
There's no option letter for loading them, and the extension is checked
case sensitively. Lua modules are loaded after CON translated to Lua.
Also remove -testlua option.

git-svn-id: https://svn.eduke32.com/eduke32@3788 1a8010ca-5511-0410-912e-c29ae57300e0
2013-05-19 19:29:16 +00:00

525 lines
17 KiB
Text

-- test script for ELua/Lunatic Interpreter
--do return end
-- error=nil -- must not affect "require"
local string = require("string")
local bit = require("bit")
local math = require("math")
local pcall = pcall
local DBG_ = DBG_
print('---=== ELua Test script ===---')
local function printf(fmt, ...)
print(string.format(fmt, ...))
end
local function checkfail(funcstr, expectedmsg)
local status, errmsg = pcall(DBG_.loadstring(funcstr))
if (status) then
print('^21ERROR:^O '..funcstr.." DIDN'T fail")
else
if (expectedmsg==nil or string.find(errmsg, expectedmsg, 1, true)) then
print("^11SUCCESS:^O "..funcstr.." failed: "..errmsg)
else
-- XXX: beginning with "^10" is counted as error in OSD_Printf()
print("^10ERROR*:^O "..funcstr.." failed: "..errmsg..
", but expected error message was: "..expectedmsg)
end
end
end
--[[
---- check serialization ----
-- test nans, infs, precision, subnorms, booleans
ourvar2 = { "asd"; 0/0, 1/0, -1/0, 0.12345678901234567, 1e-314, true }
ourvar = { ourvar2; 1, 2, 3, "qwe"; [true]=0, [false]=1 }
ourvar[#ourvar+1] = ourvar;
--]]
print('tweaking sector pals')
print('numsectors: ' .. gv.numsectors .. ' of ' .. gv.MAXSECTORS)
---[[
for i = 0, gv.numsectors/2 do
sector[i].floorpal = 1;
sector[i].ceilingpal = 2;
end
local vol, lev = gv.currentEpisode()+1, gv.currentLevel()+1
printf('volume=%d, level=%d', vol, lev)
if (vol==1 and lev==1) then -- E1L1
print('tweaking some sprites 2')
local i = 562
spriteext[i].alpha = 0.5;
sprite[i].cstat = bit.bor(sprite[i].cstat, 2+512);
spriteext[i].pitch = 128;
spriteext[i].roll = 256;
i = 107 -- pistol ammo at rooftop
spriteext[i].pitch = 128;
spriteext[i].roll = 256;
for spr in spritesofsect(307) do -- some fence sprites in E1L1
printf('spr %d', spr)
sprite[spr].pal = 6
end
--this is a problem
--actor = {}
actor[562].flags = bit.bor(actor[562].flags, 2); -- pal 6 with goggles on front SEENINE
end
--[[
-- TODO: better API for all TROR things?
if (vol==1 and lev==8) then
print('tweaking bunch 1');
-- trueror1.map
for i in sectorsofbunch(1, gv.CEILING) do
sector[i].ceilingz = sector[i].ceilingz - 3*1024;
end
for i in sectorsofbunch(1, gv.FLOOR) do
sector[i].floorz = sector[i].floorz - 3*1024;
end
end
--]]
local unsafe = pcall(function() string.UNSAFE=true; end)
--]]
--tostring = nil -- REMEMBER
--DBG_.printkv('_G in test.elua', _G)
-- direct gv array access forbidden
checkfail('gv.sprite[0].yrepeat = 100', "access forbidden")
checkfail('print(sprite[100000].ceilingpal)', "out-of-bounds sprite[] read access")
checkfail('print(gv.sprite[0])', "access forbidden")
-- set metatable forbidden
checkfail('setmetatable(sprite, {})', "attempt to read undeclared variable 'setmetatable'")
-- OOB write access.
-- Note that indexing ("reading") sector fails, even if the user wants to
-- assign to a sector member. Potentially confusing error message.
checkfail('sector[-1].ceilingpal = 4', "out-of-bounds sector[] read access")
-- wallnum member is read-only
checkfail('sector[0].wallnum = 0', "attempt to write to constant location") -- this comes from LJ/FFI
-- gv.numsectors is read-only
checkfail('gv.numsectors = 4', "attempt to write to constant location")
-- cannot create new fields in 'gv'
checkfail('gv.QWE = 4', "write access forbidden")
-- direct sector write access forbidden
checkfail('sector[4] = sector[6]', "cannot write directly to sector[]")
-- that would be horrible...
checkfail('sprite._nextspritesect[4] = -666', "cannot write directly to nextspritesect[]")
-- we're indexing a plain array!
checkfail('print(sprite._nextspritesect[4].whatfield)', "attempt to index a number value")
-- creating new keys forbidden... handled by LuaJIT
checkfail('wall[4].QWE = 123', "has no member named 'QWE'")
-- our 'require' has only safe stuff
--checkfail("require('os')")
-- we must declare globals with 'gamevar'
checkfail("new_global = 345", "attempt to write to undeclared variable 'new_global'")
-- can't redefine constants in 'gv'
checkfail('gv.CEILING = 3', "attempt to write to constant location")
-- string.dump is unavailable
checkfail('local s=require[[string]]; local tmp=s.dump(gameevent)',
"attempt to call field 'dump' (a nil value)")
if (not unsafe) then
-- changing base module tables is disallowed
checkfail('local s=require[[string]]; s.format=nil', "modifying base module table forbidden")
else
print('WARNING: RUNNING WITH UNPROTECTED BASE MODULES')
end
print('')
-- This is problematic, even though pretty much every access will yield a
-- "missing declaration" error.
-- See http://luajit.org/ext_ffi_api.html#ffi_C about what stuff ffi.C contains.
checkfail('gv.luaJIT_setmode(nil, 0, 0)', "missing declaration for symbol 'luaJIT_setmode'")
checkfail('gv.luaJIT_BC_con_lang', "attempt to call a nil value")
checkfail('gv.gethitickms = nil', "attempt to write to constant location")
-- actor[].t_data[] is not accessible for now
checkfail('local i = actor[0].t_data[15]', "has no member named 't_data'")
-- no pointer arithmetic!
checkfail('local spr = sprite[0]; local x=spr+1', "attempt to perform arithmetic on")
checkfail('gameactor(1680, 0)', "invalid last argument to gameactor: must be a function")
checkfail("do local bt=require'test/test_bitar'; bt.QWE=1; end", "modifying module table forbidden")
-- the cdata returned by player[] can't be made into a pointer!
checkfail("do local pl=player[0]; i=pl[1]; end")
checkfail("do local ud=gv.ud.camerasprite; end", "access forbidden") -- test for proper decl()
checkfail("sprite[0]:set_picnum(-10)", "invalid tile number")
checkfail("gv.g_sizes_of=nil; print(gv.g_sizes_of[0])", "write access forbidden")
checkfail("gv.cam.sect=-1", "invalid sector number")
checkfail("local flag=gv.SFLAG_NULL", "missing declaration")
player[0].wackedbyactor = -1 -- should succeed
checkfail("player[0].curr_weapon = -1", "Invalid weapon ID")
player[0].curr_weapon = 1
printf('ceilingbunch of sector 0: %d', sector[0].ceilingbunch)
gameevent(gv.EVENT_JUMP,
function(actori, playeri, dist)
printf("jump i=%d p=%d d=%d", actori, playeri, dist)
error("greetings from EVENT_JUMP")
end
)
gameevent("PROCESSINPUT",
-- Input test.
-- NOTE: I don't think that exposing g_player[].sync (aka "input") is a good idea...
function(actori, playeri, dist)
local IB = player.INPUT_BITS
local input = player[playeri]._input
if (bit.band(input.bits, IB.JUMP) ~= 0) then
print("JUMPED")
-- ... because for example this doesn't work
-- (P_HandleSharedKeys, where the JETPACK bit is tested, runs
-- before P_ProcessInput):
input.bits = bit.bor(input.bits, IB.JETPACK)
end
end
)
-- test event chaining
gameevent("JUMP", actor.FLAGS.chain_beg,
function(actori, playeri, dist)
local ps = player[playeri]
print("I'm first!")
-- DBG_.oom()
ps.weapon.PISTOL.shoots = 2605 -- RPG
ps.weapon[gv.PISTOL_WEAPON].firesound = 351 -- thunder
-- XXX: provide either named constants or methods?
ps.pipebombControl = 2
ps.tripbombControl = 2
-- Test of INTERNAL member _pals.
-- NOTE: setting colors partially is bad! E.g. after an item is
-- picked up, col[0] and col[1] remain and tint everything greenish.
ps._pals[2] = 20
ps._pals.f = 30
end
)
gameevent(gv.EVENT_ENTERLEVEL,
function()
if (gv._DEBUG_LUNATIC) then
DBG_.testmembread()
end
-- NOTE: times are for helixhorned (Core2Duo 3GHz)
local i
local N = 1e6
local t = gv.gethitickms()
for i=3,N do
gv.gethitickms()
end
t = gv.gethitickms()-t
-- x86_64: 35ns/call, x86: 280 ns/call
-- Windows 32-bit: about 1 us/call?
printf("%d gethitickms() calls took %.03f ms (%.03f us/call)",
N, t, (t*1000)/N)
local sum=0
t = gv.gethitickms()
for i=1,N do sum = sum+gv.ksqrt(i) end
t = gv.gethitickms()-t
-- x86_64: 14ns/call
printf("%d ksqrt() calls took %.03f ms (%.03f us/call) [sum=%f]",
N, t, (t*1000)/N, sum)
sum=0
t = gv.gethitickms()
for i=1,N do sum = sum+math.sqrt(i) end
t = gv.gethitickms()-t
-- x86_64: 7ns/call
printf("%d math.sqrt() calls took %.03f ms (%.03f us/call) [sum=%f]",
N, t, (t*1000)/N, sum)
printf("sqrt(0xffffffff) = %f(ksqrt) %f(math.sqrt)",
gv.ksqrt(0xffffffff), math.sqrt(0xffffffff))
local pl = player[0]
-- MAX < current is "allowed"
pl.max_ammo_amount[gv.RPG_WEAPON] = 17
pl:give_weapon(gv.RPG_WEAPON)
pl.ammo_amount[gv.RPG_WEAPON] = 54
pl:give_weapon(gv.SHRINKER_WEAPON)
-- This looks much prettier:
pl.ammo_amount.SHRINKER = 2
-- MORTER2 from test/weaponvars.con
player[0].weapon.SHOTGUN.shoots = 1653
local proj = projectile[1653]
proj.drop = 0
proj:set_trail(2329) -- SMALLSMOKE
t = gv.gethitickms()
local N=1
for n=1,N do
for i=0,gv.MAXSPRITES-1 do
sprite[i].filler = 1
end
for i=gv.MAXSPRITES-1,0,-1 do
sprite[i].shade = 1
end
for i=0,gv.MAXSPRITES-1 do
sprite[i].xoffset = 0
end
for i=gv.MAXSPRITES-1,0,-1 do
sprite[i].yoffset = 1
end
end
t = gv.gethitickms()-t
printf("%d x four 0..MAXSPRITES-1 iterations took %.03f us per outer iteration", N, (1000*t)/N)
-- Results on x86:
-- N=1: 480-1000 us (too large variance)
-- N=10: 190-210 us * 10 = 1.9-2.1 ms
-- N=100: about 160 us * 100 = about 16 ms
-- Make the DUKECAR in E1L1 into a zombie actor (temporarily)
if (sprite[24].picnum==2491) then
sprite.changestat(24, gv.STAT_ZOMBIEACTOR)
end
checkfail("gameevent('GAME', function() print('qwe') end)",
"must be called from top level")
end
)
local geom = require "geom"
gameevent("LOADACTOR", function(i)
local spr = sprite[i]
if (i==614 and spr.picnum==930) then
-- "police line" ribbon in E1L1
-- clear the hitag so that it doesn't spawn as FALLER from premap
spr.hitag = 0
end
end)
gameactor(930, nil, 1, function(i) -- "police line" ribbon
local spr = sprite[i]
local r = math.random
local d = 20
-- NOTE: __add metamethod is called because of the RHS:
local v = spr + geom.vec3(r(-d,d), r(-d,d))
spr:setpos(v)
end)
local stat = require("stat")
local hs = stat.new()
local con = require("con")
local AC, MV = con.AC, con.MV
con.action("TROOPSTAND",0,1,5,1,1)
con.action("TROOPFLINTCH", 50, 1, 1, 1, 6)
con.move("SHRUNKVELS", 32)
con.ai("AITEMP", AC.TROOPFLINTCH, MV.SHRUNKVELS, 0) -- TODO: test
-- This should work as well. In fact, it's exactly the same, but I prefer the
-- above version for clarity. NOTE: we'll need to think whether it's OK to
-- redefine composites (moves/actions/ais) during gameplay (probably not).
-- Or will we allow only one definition per label ever?
con.ai("AITEMP", "TROOPFLINTCH", "SHRUNKVELS", 0)
local TROOPSTRENGTH = 30
local AF = actor.FLAGS
local CS = sprite.CSTAT
-- Also test actor code chaining: strength is doubled.
gameactor(1680, AF.chain_end+AF.enemy, 2*TROOPSTRENGTH, "TROOPSTAND", -- LIZTROOP
function(i, playeri, dist)
spriteext[i]:make_animated()
sprite[i].pal = math.random(32)
-- sprite[i].ang = bit.band(sprite[i].ang-20, 2047)
local spr = sprite[i]
local t = gv.gethitickms()
local hit = hitscan(spr, spr.sectnum, 10, 10, 0, gv.CLIPMASK0)
hs:add(1000*(gv.gethitickms()-t))
if (hs.n == 300) then
printf("hitscan: %s", hs:getstatstr())
hs:reset()
error("greetings from LIZTROOP actor")
end
local actr = actor[i]
if (actr:get_count() % 30 == 0) then
spr.cstatx:flip(CS.YFLIP)
end
actr.flagsx:test(AF.NVG) -- test of bitint's ":test()" for actor[].flags
if (dist < 4096) then
-- Duke Vader / Anakin Nukewalker?
actor[i]:set_action("TROOPFLINTCH")
actor[i]:set_move("SHRUNKVELS")
if (dist < 1024) then
con.killit()
end
end
end
)
gameevent("DISPLAYROOMS",
function()
local cam = gv.cam
cam.pos.z = cam.pos.z + 64*16*math.sin(gv.totalclock/30)
local ps = player[0]
if (ps.cursectnum >= 0) then
local hit = sector[ps.cursectnum]:zrangeat(cam.pos)
if (gv.totalclock%200==0) then
printf("hit %s %d at z=%d, %s %d at z=%d",
hit.c.spritep and "sprite" or "sector", hit.c.num, hit.c.z,
hit.f.spritep and "sprite" or "sector", hit.f.num, hit.f.z)
end
end
end
)
gameevent("DISPLAYREST", function()
for i=0,10 do
for j=1,100 do
-- XXX: This is slower in the Polymodes than with classic!
-- con._gametext(2822, j, i, 12, 0, 0, 16, 0,0,gv.xdim,gv.ydim,8192)
end
end
-- Clear showing every sector with the pavement floor tile. (Unless we're
-- in such a sector or an adjoining one.)
-- XXX: We need a better place to do this, maybe an event in
-- G_DisplayRest() where show2dsector[] is tweaked?
local show2dsector = sector.showbitmap
for i=0,gv.numsectors-1 do
if (sector[i].floorpicnum==815) then
show2dsector:set0(i)
end
end
end)
-- APLAYER
gameactor(1405, actor.FLAGS.chain_beg, function(aci, pli)
if (con._squished(aci, pli)) then
printf("Squished LunaCON test")
end
end)
gameevent("ANIMATESPRITES",
function(aci)
local tspr = atsprite[aci]
if (tspr:getspr().picnum==1680) then
local tspr2 = tspr:dup()
if (tspr2) then
tspr2.x = tspr2.x + 512*math.cos(gv.totalclock/60)
tspr2.y = tspr2.y + 512*math.sin(gv.totalclock/60)
tspr2.cstatx:set(CS.TRANSLUCENT_BOTH_BITS)
end
end
end)
printf("EVENT_INIT = %d", gv.EVENT_INIT) -- tests default defines
local bittest = require "test/test_bitar"
bittest.sieve()
require("test/test_geom")
require("test/test_rotspr")
do
-- Test ksin vs. sinb
local xmath = require "xmath"
local sum = 0
local N = 1000
local t = gv.gethitickms()
for i=0,N*2048-1 do
sum = sum+xmath.ksin(i)
end
t = gv.gethitickms()-t
sum = sum*1e12
printf("ksin: sum*1e12=%.03f, %.03fus per 0-2047 cycle", sum, t)
sum = 0
local t = gv.gethitickms()
for i=0,N*2048-1 do
sum = sum+xmath.sinb(i)
end
t = gv.gethitickms()-t
sum = sum*1e12
printf("sinb: sum*1e12=%.03f, %.03fus per 0-2047 cycle", sum, t)
-- Results (helixhorned x86):
-- ksin: sum*1e12=0.000, 3.801us per 0-2047 cycle
-- sinb: sum*1e12=0.052, 2.886us per 0-2047 cycle
end
do
-- Test getflorzofslopeptr()/sector[]:floorzat()
local N = 100
local t = gv.gethitickms()
for n=1,N do
for i=0,gv.numsectors-1 do
local sec = sector[i]
local w1 = sec.wallptr
sec:floorzat(wall[w1])
end
end
printf("%d x %d floorzat: %.03f us", N, gv.numsectors, (gv.gethitickms()-t)*1000)
-- Results for 100 x 325 floorzat (helixhorned x86):
-- cdecl getflorzofslope(): 572.165 us
-- __fastcall getflorzofslope(): 830.147 us (sic, but tested only once!)
end
print('---=== END TEST SCRIPT ===---')
--[[
function check_sector_idx()
error("bla")
end
spritesofsect(0)
--]]
--DBG_.oom()
-- This will complain about wrong usage of 'error'. In particular,
-- the nil must not propagate to C!
checkfail('error(nil)', "error using 'error': error message must be a string")