1# testing special comment on first line
  2-- $Id: testes/main.lua $
  3-- See Copyright Notice in file all.lua
  4
  5-- most (all?) tests here assume a reasonable "Unix-like" shell
  6if _port then return end
  7
  8-- use only "double quotes" inside shell scripts (better change to
  9-- run on Windows)
 10
 11
 12print ("testing stand-alone interpreter")
 13
 14assert(os.execute())   -- machine has a system command
 15
 16local arg = arg or ARG
 17
 18local prog = os.tmpname()
 19local otherprog = os.tmpname()
 20local out = os.tmpname()
 21
 22local progname
 23do
 24  local i = 0
 25  while arg[i] do i=i-1 end
 26  progname = arg[i+1]
 27end
 28print("progname: "..progname)
 29
 30
 31local prepfile = function (s, mod, p)
 32  mod = mod and "wb" or "w"    -- mod true means binary files
 33  p = p or prog                -- file to write the program
 34  local f = io.open(p, mod)
 35  f:write(s)
 36  assert(f:close())
 37end
 38
 39local function getoutput ()
 40  local f = io.open(out)
 41  local t = f:read("a")
 42  f:close()
 43  assert(os.remove(out))
 44  return t
 45end
 46
 47local function checkprogout (s)
 48  -- expected result must end with new line
 49  assert(string.sub(s, -1) == "\n")
 50  local t = getoutput()
 51  for line in string.gmatch(s, ".-\n") do
 52    assert(string.find(t, line, 1, true))
 53  end
 54end
 55
 56local function checkout (s)
 57  local t = getoutput()
 58  if s ~= t then print(string.format("'%s' - '%s'\n", s, t)) end
 59  assert(s == t)
 60  return t
 61end
 62
 63
 64local function RUN (p, ...)
 65  p = string.gsub(p, "lua", '"'..progname..'"', 1)
 66  local s = string.format(p, ...)
 67  assert(os.execute(s))
 68end
 69
 70
 71local function NoRun (msg, p, ...)
 72  p = string.gsub(p, "lua", '"'..progname..'"', 1)
 73  local s = string.format(p, ...)
 74  s = string.format("%s >%s 2>&1", s, out)  -- send output and error to 'out'
 75  assert(not os.execute(s))
 76  assert(string.find(getoutput(), msg, 1, true))  -- check error message
 77end
 78
 79RUN('lua -v')
 80
 81print(string.format("(temporary program file used in these tests: %s)", prog))
 82
 83-- running stdin as a file
 84prepfile""
 85RUN('lua - < %s > %s', prog, out)
 86checkout("")
 87
 88prepfile[[
 89  print(
 901, a
 91)
 92]]
 93RUN('lua - < %s > %s', prog, out)
 94checkout("1\tnil\n")
 95
 96RUN('echo "print(10)\nprint(2)\n" | lua > %s', out)
 97checkout("10\n2\n")
 98
 99
100-- testing BOM
101prepfile("\xEF\xBB\xBF")
102RUN('lua %s > %s', prog, out)
103checkout("")
104
105prepfile("\xEF\xBB\xBFprint(3)")
106RUN('lua %s > %s', prog, out)
107checkout("3\n")
108
109prepfile("\xEF\xBB\xBF# comment!!\nprint(3)")
110RUN('lua %s > %s', prog, out)
111checkout("3\n")
112
113-- bad BOMs
114prepfile("\xEF", true)
115NoRun("unexpected symbol", 'lua %s', prog)
116
117prepfile("\xEF\xBB", true)
118NoRun("unexpected symbol", 'lua %s', prog)
119
120prepfile("\xEFprint(3)", true)
121NoRun("unexpected symbol", 'lua %s', prog)
122
123prepfile("\xEF\xBBprint(3)", true)
124NoRun("unexpected symbol", 'lua %s', prog)
125
126
127-- test option '-'
128RUN('echo "print(arg[1])" | lua - -h > %s', out)
129checkout("-h\n")
130
131-- test environment variables used by Lua
132
133prepfile("print(package.path)")
134
135-- test LUA_PATH
136RUN('env LUA_INIT= LUA_PATH=x lua %s > %s', prog, out)
137checkout("x\n")
138
139-- test LUA_PATH_version
140RUN('env LUA_INIT= LUA_PATH_5_4=y LUA_PATH=x lua %s > %s', prog, out)
141checkout("y\n")
142
143-- test LUA_CPATH
144prepfile("print(package.cpath)")
145RUN('env LUA_INIT= LUA_CPATH=xuxu lua %s > %s', prog, out)
146checkout("xuxu\n")
147
148-- test LUA_CPATH_version
149RUN('env LUA_INIT= LUA_CPATH_5_4=yacc LUA_CPATH=x lua %s > %s', prog, out)
150checkout("yacc\n")
151
152-- test LUA_INIT (and its access to 'arg' table)
153prepfile("print(X)")
154RUN('env LUA_INIT="X=tonumber(arg[1])" lua %s 3.2 > %s', prog, out)
155checkout("3.2\n")
156
157-- test LUA_INIT_version
158prepfile("print(X)")
159RUN('env LUA_INIT_5_4="X=10" LUA_INIT="X=3" lua %s > %s', prog, out)
160checkout("10\n")
161
162-- test LUA_INIT for files
163prepfile("x = x or 10; print(x); x = x + 1")
164RUN('env LUA_INIT="@%s" lua %s > %s', prog, prog, out)
165checkout("10\n11\n")
166
167-- test errors in LUA_INIT
168NoRun('LUA_INIT:1: msg', 'env LUA_INIT="error(\'msg\')" lua')
169
170-- test option '-E'
171local defaultpath, defaultCpath
172
173do
174  prepfile("print(package.path, package.cpath)")
175  RUN('env LUA_INIT="error(10)" LUA_PATH=xxx LUA_CPATH=xxx lua -E %s > %s',
176       prog, out)
177  local output = getoutput()
178  defaultpath = string.match(output, "^(.-)\t")
179  defaultCpath = string.match(output, "\t(.-)$")
180
181  -- running with an empty environment
182  RUN('env -i lua %s > %s', prog, out)
183  local out = getoutput()
184  assert(defaultpath == string.match(output, "^(.-)\t"))
185  assert(defaultCpath == string.match(output, "\t(.-)$"))
186end
187
188-- paths did not change
189assert(not string.find(defaultpath, "xxx") and
190       string.find(defaultpath, "lua") and
191       not string.find(defaultCpath, "xxx") and
192       string.find(defaultCpath, "lua"))
193
194
195-- test replacement of ';;' to default path
196local function convert (p)
197  prepfile("print(package.path)")
198  RUN('env LUA_PATH="%s" lua %s > %s', p, prog, out)
199  local expected = getoutput()
200  expected = string.sub(expected, 1, -2)   -- cut final end of line
201  if string.find(p, ";;") then
202    p = string.gsub(p, ";;", ";"..defaultpath..";")
203    p = string.gsub(p, "^;", "")   -- remove ';' at the beginning
204    p = string.gsub(p, ";$", "")   -- remove ';' at the end
205  end
206  assert(p == expected)
207end
208
209convert(";")
210convert(";;")
211convert("a;;b")
212convert(";;b")
213convert("a;;")
214convert("a;b;;c")
215
216
217-- test -l over multiple libraries
218prepfile("print(1); a=2; return {x=15}")
219prepfile(("print(a); print(_G['%s'].x)"):format(prog), false, otherprog)
220RUN('env LUA_PATH="?;;" lua -l %s -l%s -lstring -l io %s > %s', prog, otherprog, otherprog, out)
221checkout("1\n2\n15\n2\n15\n")
222
223-- test explicit global names in -l
224prepfile("print(str.upper'alo alo', m.max(10, 20))")
225RUN("lua -l 'str=string' '-lm=math' -e 'print(m.sin(0))' %s > %s", prog, out)
226checkout("0.0\nALO ALO\t20\n")
227
228
229-- test module names with version sufix ("libs/lib2-v2")
230RUN("env LUA_CPATH='./libs/?.so' lua -l lib2-v2 -e 'print(lib2.id())' > %s",
231    out)
232checkout("true\n")
233
234
235-- test 'arg' table
236local a = [[
237  assert(#arg == 3 and arg[1] == 'a' and
238         arg[2] == 'b' and arg[3] == 'c')
239  assert(arg[-1] == '--' and arg[-2] == "-e " and arg[-3] == '%s')
240  assert(arg[4] == undef and arg[-4] == undef)
241  local a, b, c = ...
242  assert(... == 'a' and a == 'a' and b == 'b' and c == 'c')
243]]
244a = string.format(a, progname)
245prepfile(a)
246RUN('lua "-e " -- %s a b c', prog)   -- "-e " runs an empty command
247
248-- test 'arg' availability in libraries
249prepfile"assert(arg)"
250prepfile("assert(arg)", false, otherprog)
251RUN('env LUA_PATH="?;;" lua -l%s - < %s', prog, otherprog)
252
253-- test messing up the 'arg' table
254RUN('echo "print(...)" | lua -e "arg[1] = 100" - > %s', out)
255checkout("100\n")
256NoRun("'arg' is not a table", 'echo "" | lua -e "arg = 1" -')
257
258-- test error in 'print'
259RUN('echo 10 | lua -e "print=nil" -i > /dev/null 2> %s', out)
260assert(string.find(getoutput(), "error calling 'print'"))
261
262-- test 'debug.debug'
263RUN('echo "io.stderr:write(1000)\ncont" | lua -e "require\'debug\'.debug()" 2> %s', out)
264checkout("lua_debug> 1000lua_debug> ")
265
266
267print("testing warnings")
268
269-- no warnings by default
270RUN('echo "io.stderr:write(1); warn[[XXX]]" | lua 2> %s', out)
271checkout("1")
272
273prepfile[[
274warn("@allow")               -- unknown control, ignored
275warn("@off", "XXX", "@off")  -- these are not control messages
276warn("@off")                 -- this one is
277warn("@on", "YYY", "@on")    -- not control, but warn is off
278warn("@off")                 -- keep it off
279warn("@on")                  -- restart warnings
280warn("", "@on")              -- again, no control, real warning
281warn("@on")                  -- keep it "started"
282warn("Z", "Z", "Z")          -- common warning
283]]
284RUN('lua -W %s 2> %s', prog, out)
285checkout[[
286Lua warning: @offXXX@off
287Lua warning: @on
288Lua warning: ZZZ
289]]
290
291prepfile[[
292warn("@allow")
293-- create two objects to be finalized when closing state
294-- the errors in the finalizers must generate warnings
295u1 = setmetatable({}, {__gc = function () error("XYZ") end})
296u2 = setmetatable({}, {__gc = function () error("ZYX") end})
297]]
298RUN('lua -W %s 2> %s', prog, out)
299checkprogout("ZYX)\nXYZ)\n")
300
301-- bug since 5.2: finalizer called when closing a state could
302-- subvert finalization order
303prepfile[[
304-- should be called last
305print("creating 1")
306setmetatable({}, {__gc = function () print(1) end})
307
308print("creating 2")
309setmetatable({}, {__gc = function ()
310  print("2")
311  print("creating 3")
312  -- this finalizer should not be called, as object will be
313  -- created after 'lua_close' has been called
314  setmetatable({}, {__gc = function () print(3) end})
315  print(collectgarbage())    -- cannot call collector here
316  os.exit(0, true)
317end})
318]]
319RUN('lua -W %s > %s', prog, out)
320checkout[[
321creating 1
322creating 2
3232
324creating 3
325nil
3261
327]]
328
329
330-- test many arguments
331prepfile[[print(({...})[30])]]
332RUN('lua %s %s > %s', prog, string.rep(" a", 30), out)
333checkout("a\n")
334
335RUN([[lua "-eprint(1)" -ea=3 -e "print(a)" > %s]], out)
336checkout("1\n3\n")
337
338-- test iteractive mode
339prepfile[[
340(6*2-6) -- ===
341a =
34210
343print(a)
344a]]
345RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out)
346checkprogout("6\n10\n10\n\n")
347
348prepfile("a = [[b\nc\nd\ne]]\n=a")
349RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out)
350checkprogout("b\nc\nd\ne\n\n")
351
352local prompt = "alo"
353prepfile[[ --
354a = 2
355]]
356RUN([[lua "-e_PROMPT='%s'" -i < %s > %s]], prompt, prog, out)
357local t = getoutput()
358assert(string.find(t, prompt .. ".*" .. prompt .. ".*" .. prompt))
359
360-- using the prompt default
361prepfile[[ --
362a = 2
363]]
364RUN([[lua -i < %s > %s]], prog, out)
365local t = getoutput()
366prompt = "> "    -- the default
367assert(string.find(t, prompt .. ".*" .. prompt .. ".*" .. prompt))
368
369
370-- non-string prompt
371prompt =
372  "local C = 0;\z
373   _PROMPT=setmetatable({},{__tostring = function () \z
374     C = C + 1; return C end})"
375prepfile[[ --
376a = 2
377]]
378RUN([[lua -e "%s" -i < %s > %s]], prompt, prog, out)
379local t = getoutput()
380assert(string.find(t, [[
3811 --
3822a = 2
3833
384]], 1, true))
385
386
387-- test for error objects
388prepfile[[
389debug = require "debug"
390m = {x=0}
391setmetatable(m, {__tostring = function(x)
392  return tostring(debug.getinfo(4).currentline + x.x)
393end})
394error(m)
395]]
396NoRun(progname .. ": 6\n", [[lua %s]], prog)
397
398prepfile("error{}")
399NoRun("error object is a table value", [[lua %s]], prog)
400
401
402-- chunk broken in many lines
403local s = [=[ --
404function f ( x )
405  local a = [[
406xuxu
407]]
408  local b = "\
409xuxu\n"
410  if x == 11 then return 1 + 12 , 2 + 20 end  --[[ test multiple returns ]]
411  return x + 1
412  --\\
413end
414return( f( 100 ) )
415assert( a == b )
416do return f( 11 ) end  ]=]
417s = string.gsub(s, ' ', '\n\n')   -- change all spaces for newlines
418prepfile(s)
419RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out)
420checkprogout("101\n13\t22\n\n")
421
422prepfile[[#comment in 1st line without \n at the end]]
423RUN('lua %s', prog)
424
425-- first-line comment with binary file
426prepfile("#comment\n" .. string.dump(load("print(3)")), true)
427RUN('lua %s > %s', prog, out)
428checkout('3\n')
429
430-- close Lua with an open file
431prepfile(string.format([[io.output(%q); io.write('alo')]], out))
432RUN('lua %s', prog)
433checkout('alo')
434
435-- bug in 5.2 beta (extra \0 after version line)
436RUN([[lua -v  -e"print'hello'" > %s]], out)
437t = getoutput()
438assert(string.find(t, "PUC%-Rio\nhello"))
439
440
441-- testing os.exit
442prepfile("os.exit(nil, true)")
443RUN('lua %s', prog)
444prepfile("os.exit(0, true)")
445RUN('lua %s', prog)
446prepfile("os.exit(true, true)")
447RUN('lua %s', prog)
448prepfile("os.exit(1, true)")
449NoRun("", "lua %s", prog)   -- no message
450prepfile("os.exit(false, true)")
451NoRun("", "lua %s", prog)   -- no message
452
453
454-- to-be-closed variables in main chunk
455prepfile[[
456  local x <close> = setmetatable({},
457        {__close = function (self, err)
458                     assert(err == nil)
459                     print("Ok")
460                   end})
461  local e1 <close> = setmetatable({}, {__close = function () print(120) end})
462  os.exit(true, true)
463]]
464RUN('lua %s > %s', prog, out)
465checkprogout("120\nOk\n")
466
467
468-- remove temporary files
469assert(os.remove(prog))
470assert(os.remove(otherprog))
471assert(not os.remove(out))
472
473-- invalid options
474NoRun("unrecognized option '-h'", "lua -h")
475NoRun("unrecognized option '---'", "lua ---")
476NoRun("unrecognized option '-Ex'", "lua -Ex")
477NoRun("unrecognized option '-vv'", "lua -vv")
478NoRun("unrecognized option '-iv'", "lua -iv")
479NoRun("'-e' needs argument", "lua -e")
480NoRun("syntax error", "lua -e a")
481NoRun("'-l' needs argument", "lua -l")
482
483
484if T then   -- test library?
485  print("testing 'not enough memory' to create a state")
486  NoRun("not enough memory", "env MEMLIMIT=100 lua")
487
488  -- testing 'warn'
489  warn("@store")
490  warn("@123", "456", "789")
491  assert(_WARN == "@123456789"); _WARN = false
492
493  warn("zip", "", " ", "zap")
494  assert(_WARN == "zip zap"); _WARN = false
495  warn("ZIP", "", " ", "ZAP")
496  assert(_WARN == "ZIP ZAP"); _WARN = false
497  warn("@normal")
498end
499
500do
501  -- 'warn' must get at least one argument
502  local st, msg = pcall(warn)
503  assert(string.find(msg, "string expected"))
504
505  -- 'warn' does not leave unfinished warning in case of errors
506  -- (message would appear in next warning)
507  st, msg = pcall(warn, "SHOULD NOT APPEAR", {})
508  assert(string.find(msg, "string expected"))
509end
510
511print('+')
512
513print('testing Ctrl C')
514do
515  -- interrupt a script
516  local function kill (pid)
517    return os.execute(string.format('kill -INT %s 2> /dev/null', pid))
518  end
519
520  -- function to run a script in background, returning its output file
521  -- descriptor and its pid
522  local function runback (luaprg)
523    -- shell script to run 'luaprg' in background and echo its pid
524    local shellprg = string.format('%s -e "%s" & echo $!', progname, luaprg)
525    local f = io.popen(shellprg, "r")   -- run shell script
526    local pid = f:read()   -- get pid for Lua script
527    print("(if test fails now, it may leave a Lua script running in \z
528            background, pid " .. pid .. ")")
529    return f, pid
530  end
531
532  -- Lua script that runs protected infinite loop and then prints '42'
533  local f, pid = runback[[
534    pcall(function () print(12); while true do end end); print(42)]]
535  -- wait until script is inside 'pcall'
536  assert(f:read() == "12")
537  kill(pid)  -- send INT signal to Lua script
538  -- check that 'pcall' captured the exception and script continued running
539  assert(f:read() == "42")  -- expected output
540  assert(f:close())
541  print("done")
542
543  -- Lua script in a long unbreakable search
544  local f, pid = runback[[
545    print(15); string.find(string.rep('a', 100000), '.*b')]]
546  -- wait (so script can reach the loop)
547  assert(f:read() == "15")
548  assert(os.execute("sleep 1"))
549  -- must send at least two INT signals to stop this Lua script
550  local n = 100
551  for i = 0, 100 do   -- keep sending signals
552    if not kill(pid) then   -- until it fails
553      n = i   -- number of non-failed kills
554      break
555    end
556  end
557  assert(f:close())
558  assert(n >= 2)
559  print(string.format("done (with %d kills)", n))
560
561end
562
563print("OK")