lua

A copy of the Lua development repository
Log | Files | Refs | README

files.lua (26335B)


      1 -- $Id: testes/files.lua $
      2 -- See Copyright Notice in file all.lua
      3 
      4 local debug = require "debug"
      5 
      6 local maxint = math.maxinteger
      7 
      8 assert(type(os.getenv"PATH") == "string")
      9 
     10 assert(io.input(io.stdin) == io.stdin)
     11 assert(not pcall(io.input, "non-existent-file"))
     12 assert(io.output(io.stdout) == io.stdout)
     13 
     14 
     15 local function testerr (msg, f, ...)
     16   local stat, err = pcall(f, ...)
     17   return (not stat and string.find(err, msg, 1, true))
     18 end
     19 
     20 
     21 local function checkerr (msg, f, ...)
     22   assert(testerr(msg, f, ...))
     23 end
     24 
     25 
     26 -- cannot close standard files
     27 assert(not io.close(io.stdin) and
     28        not io.stdout:close() and
     29        not io.stderr:close())
     30 
     31 -- cannot call close method without an argument (new in 5.3.5)
     32 checkerr("got no value", io.stdin.close)
     33 
     34 
     35 assert(type(io.input()) == "userdata" and io.type(io.output()) == "file")
     36 assert(type(io.stdin) == "userdata" and io.type(io.stderr) == "file")
     37 assert(not io.type(8))
     38 local a = {}; setmetatable(a, {})
     39 assert(not io.type(a))
     40 
     41 assert(getmetatable(io.input()).__name == "FILE*")
     42 
     43 local a,b,c = io.open('xuxu_nao_existe')
     44 assert(not a and type(b) == "string" and type(c) == "number")
     45 
     46 a,b,c = io.open('/a/b/c/d', 'w')
     47 assert(not a and type(b) == "string" and type(c) == "number")
     48 
     49 local file = os.tmpname()
     50 local f, msg = io.open(file, "w")
     51 if not f then
     52   (Message or print)("'os.tmpname' file cannot be open; skipping file tests")
     53 
     54 else  --{  most tests here need tmpname
     55 f:close()
     56 
     57 print('testing i/o')
     58 
     59 local otherfile = os.tmpname()
     60 
     61 checkerr("invalid mode", io.open, file, "rw")
     62 checkerr("invalid mode", io.open, file, "rb+")
     63 checkerr("invalid mode", io.open, file, "r+bk")
     64 checkerr("invalid mode", io.open, file, "")
     65 checkerr("invalid mode", io.open, file, "+")
     66 checkerr("invalid mode", io.open, file, "b")
     67 assert(io.open(file, "r+b")):close()
     68 assert(io.open(file, "r+")):close()
     69 assert(io.open(file, "rb")):close()
     70 
     71 assert(os.setlocale('C', 'all'))
     72 
     73 io.input(io.stdin); io.output(io.stdout);
     74 
     75 os.remove(file)
     76 assert(not loadfile(file))
     77 -- Lua code cannot use chunks with fixed buffers
     78 checkerr("invalid mode", load, "", "", "B")
     79 checkerr("", dofile, file)
     80 assert(not io.open(file))
     81 io.output(file)
     82 assert(io.output() ~= io.stdout)
     83 
     84 if not _port then   -- invalid seek
     85   local status, msg, code = io.stdin:seek("set", 1000)
     86   assert(not status and type(msg) == "string" and type(code) == "number")
     87 end
     88 
     89 assert(io.output():seek() == 0)
     90 assert(io.write("alo alo"):seek() == string.len("alo alo"))
     91 assert(io.output():seek("cur", -3) == string.len("alo alo")-3)
     92 assert(io.write("joao"))
     93 assert(io.output():seek("end") == string.len("alo joao"))
     94 
     95 assert(io.output():seek("set") == 0)
     96 
     97 assert(io.write('"alo"', "{a}\n", "second line\n", "third line \n"))
     98 assert(io.write('Xfourth_line'))
     99 io.output(io.stdout)
    100 collectgarbage()  -- file should be closed by GC
    101 assert(io.input() == io.stdin and rawequal(io.output(), io.stdout))
    102 print('+')
    103 
    104 -- test GC for files
    105 collectgarbage()
    106 for i=1,120 do
    107   for i=1,5 do
    108     io.input(file)
    109     assert(io.open(file, 'r'))
    110     io.lines(file)
    111   end
    112   collectgarbage()
    113 end
    114 
    115 io.input():close()
    116 io.close()
    117 
    118 assert(os.rename(file, otherfile))
    119 assert(not os.rename(file, otherfile))
    120 
    121 io.output(io.open(otherfile, "ab"))
    122 assert(io.write("\n\n\t\t  ", 3450, "\n"));
    123 io.close()
    124 
    125 
    126 do
    127   -- closing file by scope
    128   local F = nil
    129   do
    130     local f <close> = assert(io.open(file, "w"))
    131     F = f
    132   end
    133   assert(tostring(F) == "file (closed)")
    134 end
    135 assert(os.remove(file))
    136 
    137 
    138 do
    139   -- test writing/reading numbers
    140   local f <close> = assert(io.open(file, "w"))
    141   f:write(maxint, '\n')
    142   f:write(string.format("0X%x\n", maxint))
    143   f:write("0xABCp-3", '\n')
    144   f:write(0, '\n')
    145   f:write(-maxint, '\n')
    146   f:write(string.format("0x%X\n", -maxint))
    147   f:write("-0xABCp-3", '\n')
    148   assert(f:close())
    149   local f <close> = assert(io.open(file, "r"))
    150   assert(f:read("n") == maxint)
    151   assert(f:read("n") == maxint)
    152   assert(f:read("n") == 0xABCp-3)
    153   assert(f:read("n") == 0)
    154   assert(f:read("*n") == -maxint)            -- test old format (with '*')
    155   assert(f:read("n") == -maxint)
    156   assert(f:read("*n") == -0xABCp-3)            -- test old format (with '*')
    157 end
    158 assert(os.remove(file))
    159 
    160 
    161 -- testing multiple arguments to io.read
    162 do
    163   local f <close> = assert(io.open(file, "w"))
    164   f:write[[
    165 a line
    166 another line
    167 1234
    168 3.45
    169 one
    170 two
    171 three
    172 ]]
    173   local l1, l2, l3, l4, n1, n2, c, dummy
    174   assert(f:close())
    175   local f <close> = assert(io.open(file, "r"))
    176   l1, l2, n1, n2, dummy = f:read("l", "L", "n", "n")
    177   assert(l1 == "a line" and l2 == "another line\n" and
    178          n1 == 1234 and n2 == 3.45 and dummy == nil)
    179   assert(f:close())
    180   local f <close> = assert(io.open(file, "r"))
    181   l1, l2, n1, n2, c, l3, l4, dummy = f:read(7, "l", "n", "n", 1, "l", "l")
    182   assert(l1 == "a line\n" and l2 == "another line" and c == '\n' and
    183          n1 == 1234 and n2 == 3.45 and l3 == "one" and l4 == "two"
    184          and dummy == nil)
    185   assert(f:close())
    186   local f <close> = assert(io.open(file, "r"))
    187   -- second item failing
    188   l1, n1, n2, dummy = f:read("l", "n", "n", "l")
    189   assert(l1 == "a line" and not n1)
    190 end
    191 assert(os.remove(file))
    192 
    193 
    194 
    195 -- test yielding during 'dofile'
    196 f = assert(io.open(file, "w"))
    197 f:write[[
    198 local x, z = coroutine.yield(10)
    199 local y = coroutine.yield(20)
    200 return x + y * z
    201 ]]
    202 assert(f:close())
    203 f = coroutine.wrap(dofile)
    204 assert(f(file) == 10)
    205 assert(f(100, 101) == 20)
    206 assert(f(200) == 100 + 200 * 101)
    207 assert(os.remove(file))
    208 
    209 
    210 f = assert(io.open(file, "w"))
    211 -- test number termination
    212 f:write[[
    213 -12.3-	-0xffff+  .3|5.E-3X  +234e+13E 0xDEADBEEFDEADBEEFx
    214 0x1.13Ap+3e
    215 ]]
    216 -- very long number
    217 f:write("1234"); for i = 1, 1000 do f:write("0") end;  f:write("\n")
    218 -- invalid sequences (must read and discard valid prefixes)
    219 f:write[[
    220 .e+	0.e;	--;  0xX;
    221 ]]
    222 assert(f:close())
    223 f = assert(io.open(file, "r"))
    224 assert(f:read("n") == -12.3); assert(f:read(1) == "-")
    225 assert(f:read("n") == -0xffff); assert(f:read(2) == "+ ")
    226 assert(f:read("n") == 0.3); assert(f:read(1) == "|")
    227 assert(f:read("n") == 5e-3); assert(f:read(1) == "X")
    228 assert(f:read("n") == 234e13); assert(f:read(1) == "E")
    229 assert(f:read("n") == 0Xdeadbeefdeadbeef); assert(f:read(2) == "x\n")
    230 assert(f:read("n") == 0x1.13aP3); assert(f:read(1) == "e")
    231 
    232 do   -- attempt to read too long number
    233   assert(not f:read("n"))  -- fails
    234   local s = f:read("L")   -- read rest of line
    235   assert(string.find(s, "^00*\n$"))  -- lots of 0's left
    236 end
    237 
    238 assert(not f:read("n")); assert(f:read(2) == "e+")
    239 assert(not f:read("n")); assert(f:read(1) == ";")
    240 assert(not f:read("n")); assert(f:read(2) == "-;")
    241 assert(not f:read("n")); assert(f:read(1) == "X")
    242 assert(not f:read("n")); assert(f:read(1) == ";")
    243 assert(not f:read("n")); assert(not f:read(0))   -- end of file
    244 assert(f:close())
    245 assert(os.remove(file))
    246 
    247 
    248 -- test line generators
    249 assert(not pcall(io.lines, "non-existent-file"))
    250 assert(os.rename(otherfile, file))
    251 io.output(otherfile)
    252 local n = 0
    253 local f = io.lines(file)
    254 while f() do n = n + 1 end;
    255 assert(n == 6)   -- number of lines in the file
    256 checkerr("file is already closed", f)
    257 checkerr("file is already closed", f)
    258 -- copy from file to otherfile
    259 n = 0
    260 for l in io.lines(file) do io.write(l, "\n"); n = n + 1 end
    261 io.close()
    262 assert(n == 6)
    263 -- copy from otherfile back to file
    264 local f = assert(io.open(otherfile))
    265 assert(io.type(f) == "file")
    266 io.output(file)
    267 assert(not io.output():read())
    268 n = 0
    269 for l in f:lines() do io.write(l, "\n"); n = n + 1 end
    270 assert(tostring(f):sub(1, 5) == "file ")
    271 assert(f:close()); io.close()
    272 assert(n == 6)
    273 checkerr("closed file", io.close, f)
    274 assert(tostring(f) == "file (closed)")
    275 assert(io.type(f) == "closed file")
    276 io.input(file)
    277 f = io.open(otherfile):lines()
    278 n = 0
    279 for l in io.lines() do assert(l == f()); n = n + 1 end
    280 f = nil; collectgarbage()
    281 assert(n == 6)
    282 assert(os.remove(otherfile))
    283 
    284 do  -- bug in 5.3.1
    285   io.output(otherfile)
    286   io.write(string.rep("a", 300), "\n")
    287   io.close()
    288   local t ={}; for i = 1, 250 do t[i] = 1 end
    289   t = {io.lines(otherfile, table.unpack(t))()}
    290   -- everything ok here
    291   assert(#t == 250 and t[1] == 'a' and t[#t] == 'a')
    292   t[#t + 1] = 1    -- one too many
    293   checkerr("too many arguments", io.lines, otherfile, table.unpack(t))
    294   collectgarbage()   -- ensure 'otherfile' is closed
    295   assert(os.remove(otherfile))
    296 end
    297 
    298 io.input(file)
    299 do  -- test error returns
    300   local a,b,c = io.input():write("xuxu")
    301   assert(not a and type(b) == "string" and type(c) == "number")
    302 end
    303 checkerr("invalid format", io.read, "x")
    304 assert(io.read(0) == "")   -- not eof
    305 assert(io.read(5, 'l') == '"alo"')
    306 assert(io.read(0) == "")
    307 assert(io.read() == "second line")
    308 local x = io.input():seek()
    309 assert(io.read() == "third line ")
    310 assert(io.input():seek("set", x))
    311 assert(io.read('L') == "third line \n")
    312 assert(io.read(1) == "X")
    313 assert(io.read(string.len"fourth_line") == "fourth_line")
    314 assert(io.input():seek("cur", -string.len"fourth_line"))
    315 assert(io.read() == "fourth_line")
    316 assert(io.read() == "")  -- empty line
    317 assert(io.read('n') == 3450)
    318 assert(io.read(1) == '\n')
    319 assert(not io.read(0))  -- end of file
    320 assert(not io.read(1))  -- end of file
    321 assert(not io.read(30000))  -- end of file
    322 assert(({io.read(1)})[2] == undef)
    323 assert(not io.read())  -- end of file
    324 assert(({io.read()})[2] == undef)
    325 assert(not io.read('n'))  -- end of file
    326 assert(({io.read('n')})[2] == undef)
    327 assert(io.read('a') == '')  -- end of file (OK for 'a')
    328 assert(io.read('a') == '')  -- end of file (OK for 'a')
    329 collectgarbage()
    330 print('+')
    331 io.close(io.input())
    332 checkerr(" input file is closed", io.read)
    333 
    334 assert(os.remove(file))
    335 
    336 local t = '0123456789'
    337 for i=1,10 do t = t..t; end
    338 assert(string.len(t) == 10*2^10)
    339 
    340 io.output(file)
    341 io.write("alo"):write("\n")
    342 io.close()
    343 checkerr(" output file is closed", io.write)
    344 local f = io.open(file, "a+b")
    345 io.output(f)
    346 collectgarbage()
    347 
    348 assert(io.write(' ' .. t .. ' '))
    349 assert(io.write(';', 'end of file\n'))
    350 f:flush(); io.flush()
    351 f:close()
    352 print('+')
    353 
    354 io.input(file)
    355 assert(io.read() == "alo")
    356 assert(io.read(1) == ' ')
    357 assert(io.read(string.len(t)) == t)
    358 assert(io.read(1) == ' ')
    359 assert(io.read(0))
    360 assert(io.read('a') == ';end of file\n')
    361 assert(not io.read(0))
    362 assert(io.close(io.input()))
    363 
    364 
    365 -- test errors in read/write
    366 do
    367   local function ismsg (m)
    368     -- error message is not a code number
    369     return (type(m) == "string" and not tonumber(m))
    370   end
    371 
    372   -- read
    373   local f = io.open(file, "w")
    374   local r, m, c = f:read()
    375   assert(not r and ismsg(m) and type(c) == "number")
    376   assert(f:close())
    377   -- write
    378   f = io.open(file, "r")
    379   r, m, c = f:write("whatever")
    380   assert(not r and ismsg(m) and type(c) == "number")
    381   assert(f:close())
    382   -- lines
    383   f = io.open(file, "w")
    384   r, m = pcall(f:lines())
    385   assert(r == false and ismsg(m))
    386   assert(f:close())
    387 end
    388 
    389 assert(os.remove(file))
    390 
    391 -- test for L format
    392 io.output(file); io.write"\n\nline\nother":close()
    393 io.input(file)
    394 assert(io.read"L" == "\n")
    395 assert(io.read"L" == "\n")
    396 assert(io.read"L" == "line\n")
    397 assert(io.read"L" == "other")
    398 assert(not io.read"L")
    399 io.input():close()
    400 
    401 local f = assert(io.open(file))
    402 local s = ""
    403 for l in f:lines("L") do s = s .. l end
    404 assert(s == "\n\nline\nother")
    405 f:close()
    406 
    407 io.input(file)
    408 s = ""
    409 for l in io.lines(nil, "L") do s = s .. l end
    410 assert(s == "\n\nline\nother")
    411 io.input():close()
    412 
    413 s = ""
    414 for l in io.lines(file, "L") do s = s .. l end
    415 assert(s == "\n\nline\nother")
    416 
    417 s = ""
    418 for l in io.lines(file, "l") do s = s .. l end
    419 assert(s == "lineother")
    420 
    421 io.output(file); io.write"a = 10 + 34\na = 2*a\na = -a\n":close()
    422 local t = {}
    423 assert(load(io.lines(file, "L"), nil, nil, t))()
    424 assert(t.a == -((10 + 34) * 2))
    425 
    426 
    427 do   -- testing closing file in line iteration
    428 
    429   -- get the to-be-closed variable from a loop
    430   local function gettoclose (lv)
    431     lv = lv + 1
    432     local stvar = 0   -- to-be-closed is 3th state variable in the loop
    433     for i = 1, 1000 do
    434       local n, v = debug.getlocal(lv, i)
    435       if n == "(for state)" then
    436         stvar = stvar + 1
    437         if stvar == 3 then return v end
    438       end
    439     end
    440   end
    441 
    442   local f
    443   for l in io.lines(file) do
    444     f = gettoclose(1)
    445     assert(io.type(f) == "file")
    446     break
    447   end
    448   assert(io.type(f) == "closed file")
    449 
    450   f = nil
    451   local function foo (name)
    452     for l in io.lines(name) do
    453       f = gettoclose(1)
    454       assert(io.type(f) == "file")
    455       error(f)   -- exit loop with an error
    456     end
    457   end
    458   local st, msg = pcall(foo, file)
    459   assert(st == false and io.type(msg) == "closed file")
    460 
    461 end
    462 
    463 
    464 -- test for multipe arguments in 'lines'
    465 io.output(file); io.write"0123456789\n":close()
    466 for a,b in io.lines(file, 1, 1) do
    467   if a == "\n" then assert(not b)
    468   else assert(tonumber(a) == tonumber(b) - 1)
    469   end
    470 end
    471 
    472 for a,b,c in io.lines(file, 1, 2, "a") do
    473   assert(a == "0" and b == "12" and c == "3456789\n")
    474 end
    475 
    476 for a,b,c in io.lines(file, "a", 0, 1) do
    477   if a == "" then break end
    478   assert(a == "0123456789\n" and not b and not c)
    479 end
    480 collectgarbage()   -- to close file in previous iteration
    481 
    482 io.output(file); io.write"00\n10\n20\n30\n40\n":close()
    483 for a, b in io.lines(file, "n", "n") do
    484   if a == 40 then assert(not b)
    485   else assert(a == b - 10)
    486   end
    487 end
    488 
    489 
    490 -- test load x lines
    491 io.output(file);
    492 io.write[[
    493 local y
    494 = X
    495 X =
    496 X *
    497 2 +
    498 X;
    499 X =
    500 X
    501 -                                   y;
    502 ]]:close()
    503 _G.X = 1
    504 assert(not load((io.lines(file))))
    505 collectgarbage()   -- to close file in previous iteration
    506 load((io.lines(file, "L")))()
    507 assert(_G.X == 2)
    508 load((io.lines(file, 1)))()
    509 assert(_G.X == 4)
    510 load((io.lines(file, 3)))()
    511 assert(_G.X == 8)
    512 _G.X = nil
    513 
    514 print('+')
    515 
    516 local x1 = "string\n\n\\com \"\"''coisas [[estranhas]] ]]'"
    517 io.output(file)
    518 assert(io.write(string.format("X2 = %q\n-- comment without ending EOS", x1)))
    519 io.close()
    520 assert(loadfile(file))()
    521 assert(x1 == _G.X2)
    522 _G.X2 = nil
    523 print('+')
    524 assert(os.remove(file))
    525 assert(not os.remove(file))
    526 assert(not os.remove(otherfile))
    527 
    528 -- testing loadfile
    529 local function testloadfile (s, expres)
    530   io.output(file)
    531   if s then io.write(s) end
    532   io.close()
    533   local res = assert(loadfile(file))()
    534   assert(os.remove(file))
    535   assert(res == expres)
    536 end
    537 
    538 -- loading empty file
    539 testloadfile(nil, nil)
    540 
    541 -- loading file with initial comment without end of line
    542 testloadfile("# a non-ending comment", nil)
    543 
    544 
    545 -- checking Unicode BOM in files
    546 testloadfile("\xEF\xBB\xBF# some comment\nreturn 234", 234)
    547 testloadfile("\xEF\xBB\xBFreturn 239", 239)
    548 testloadfile("\xEF\xBB\xBF", nil)   -- empty file with a BOM
    549 
    550 
    551 -- checking line numbers in files with initial comments
    552 testloadfile("# a comment\nreturn require'debug'.getinfo(1).currentline", 2)
    553 
    554 
    555 -- loading binary file
    556 io.output(io.open(file, "wb"))
    557 assert(io.write(string.dump(function () return 10, '\0alo\255', 'hi' end)))
    558 io.close()
    559 a, b, c = assert(loadfile(file))()
    560 assert(a == 10 and b == "\0alo\255" and c == "hi")
    561 assert(os.remove(file))
    562 
    563 -- bug in 5.2.1
    564 do
    565   io.output(io.open(file, "wb"))
    566   -- save function with no upvalues
    567   assert(io.write(string.dump(function () return 1 end)))
    568   io.close()
    569   f = assert(loadfile(file, "b", {}))
    570   assert(type(f) == "function" and f() == 1)
    571   assert(os.remove(file))
    572 end
    573 
    574 -- loading binary file with initial comment
    575 io.output(io.open(file, "wb"))
    576 assert(io.write("#this is a comment for a binary file\0\n",
    577                 string.dump(function () return 20, '\0\0\0' end)))
    578 io.close()
    579 a, b, c = assert(loadfile(file))()
    580 assert(a == 20 and b == "\0\0\0" and c == nil)
    581 assert(os.remove(file))
    582 
    583 
    584 -- 'loadfile' with 'env'
    585 do
    586   local f = io.open(file, 'w')
    587   f:write[[
    588     if (...) then a = 15; return b, c, d
    589     else return _ENV
    590     end
    591   ]]
    592   f:close()
    593   local t = {b = 12, c = "xuxu", d = print}
    594   local f = assert(loadfile(file, 't', t))
    595   local b, c, d = f(1)
    596   assert(t.a == 15 and b == 12 and c == t.c and d == print)
    597   assert(f() == t)
    598   f = assert(loadfile(file, 't', nil))
    599   assert(f() == nil)
    600   f = assert(loadfile(file))
    601   assert(f() == _G)
    602   assert(os.remove(file))
    603 end
    604 
    605 
    606 -- 'loadfile' x modes
    607 do
    608   io.open(file, 'w'):write("return 10"):close()
    609   local s, m = loadfile(file, 'b')
    610   assert(not s and string.find(m, "a text chunk"))
    611   io.open(file, 'w'):write("\27 return 10"):close()
    612   local s, m = loadfile(file, 't')
    613   assert(not s and string.find(m, "a binary chunk"))
    614   assert(os.remove(file))
    615 end
    616 
    617 
    618 io.output(file)
    619 assert(io.write("qualquer coisa\n"))
    620 assert(io.write("mais qualquer coisa"))
    621 io.close()
    622 assert(io.output(assert(io.open(otherfile, 'wb')))
    623        :write("outra coisa\0\1\3\0\0\0\0\255\0")
    624        :close())
    625 
    626 local filehandle = assert(io.open(file, 'r+'))
    627 local otherfilehandle = assert(io.open(otherfile, 'rb'))
    628 assert(filehandle ~= otherfilehandle)
    629 assert(type(filehandle) == "userdata")
    630 assert(filehandle:read('l') == "qualquer coisa")
    631 io.input(otherfilehandle)
    632 assert(io.read(string.len"outra coisa") == "outra coisa")
    633 assert(filehandle:read('l') == "mais qualquer coisa")
    634 filehandle:close();
    635 assert(type(filehandle) == "userdata")
    636 io.input(otherfilehandle)
    637 assert(io.read(4) == "\0\1\3\0")
    638 assert(io.read(3) == "\0\0\0")
    639 assert(io.read(0) == "")        -- 255 is not eof
    640 assert(io.read(1) == "\255")
    641 assert(io.read('a') == "\0")
    642 assert(not io.read(0))
    643 assert(otherfilehandle == io.input())
    644 otherfilehandle:close()
    645 assert(os.remove(file))
    646 assert(os.remove(otherfile))
    647 collectgarbage()
    648 
    649 io.output(file)
    650   :write[[
    651  123.4	-56e-2  not a number
    652 second line
    653 third line
    654 
    655 and the rest of the file
    656 ]]
    657   :close()
    658 io.input(file)
    659 local _,a,b,c,d,e,h,__ = io.read(1, 'n', 'n', 'l', 'l', 'l', 'a', 10)
    660 assert(io.close(io.input()))
    661 assert(_ == ' ' and not __)
    662 assert(type(a) == 'number' and a==123.4 and b==-56e-2)
    663 assert(d=='second line' and e=='third line')
    664 assert(h==[[
    665 
    666 and the rest of the file
    667 ]])
    668 assert(os.remove(file))
    669 collectgarbage()
    670 
    671 -- testing buffers
    672 do
    673   local f = assert(io.open(file, "w"))
    674   local fr = assert(io.open(file, "r"))
    675   assert(f:setvbuf("full", 2000))
    676   f:write("x")
    677   assert(fr:read("all") == "")  -- full buffer; output not written yet
    678   f:close()
    679   fr:seek("set")
    680   assert(fr:read("all") == "x")   -- `close' flushes it
    681   f = assert(io.open(file), "w")
    682   assert(f:setvbuf("no"))
    683   f:write("x")
    684   fr:seek("set")
    685   assert(fr:read("all") == "x")  -- no buffer; output is ready
    686   f:close()
    687   f = assert(io.open(file, "a"))
    688   assert(f:setvbuf("line"))
    689   f:write("x")
    690   fr:seek("set", 1)
    691   assert(fr:read("all") == "")   -- line buffer; no output without `\n'
    692   f:write("a\n"):seek("set", 1)
    693   assert(fr:read("all") == "xa\n")  -- now we have a whole line
    694   f:close(); fr:close()
    695   assert(os.remove(file))
    696 end
    697 
    698 
    699 if not _soft then
    700   print("testing large files (> BUFSIZ)")
    701   io.output(file)
    702   for i=1,5001 do io.write('0123456789123') end
    703   io.write('\n12346'):close()
    704   io.input(file)
    705   local x = io.read('a')
    706   io.input():seek('set', 0)
    707   local y = io.read(30001)..io.read(1005)..io.read(0)..
    708             io.read(1)..io.read(100003)
    709   assert(x == y and string.len(x) == 5001*13 + 6)
    710   io.input():seek('set', 0)
    711   y = io.read()  -- huge line
    712   assert(x == y..'\n'..io.read())
    713   assert(not io.read())
    714   io.close(io.input())
    715   assert(os.remove(file))
    716   x = nil; y = nil
    717 end
    718 
    719 if not _port then
    720   local progname
    721   do  -- get name of running executable
    722     local arg = arg or ARG
    723     local i = 0
    724     while arg[i] do i = i - 1 end
    725     progname = '"' .. arg[i + 1] .. '"'
    726   end
    727   print("testing popen/pclose and execute")
    728   -- invalid mode for popen
    729   checkerr("invalid mode", io.popen, "cat", "")
    730   checkerr("invalid mode", io.popen, "cat", "r+")
    731   checkerr("invalid mode", io.popen, "cat", "rw")
    732   do  -- basic tests for popen
    733     local file = os.tmpname()
    734     local f = assert(io.popen("cat - > " .. file, "w"))
    735     f:write("a line")
    736     assert(f:close())
    737     local f = assert(io.popen("cat - < " .. file, "r"))
    738     assert(f:read("a") == "a line")
    739     assert(f:close())
    740     assert(os.remove(file))
    741   end
    742 
    743   local tests = {
    744     -- command,   what,  code
    745     {"ls > /dev/null", "ok"},
    746     {"not-to-be-found-command", "exit"},
    747     {"exit 3", "exit", 3},
    748     {"exit 129", "exit", 129},
    749     {"kill -s HUP $$", "signal", 1},
    750     {"kill -s KILL $$", "signal", 9},
    751     {"sh -c 'kill -s HUP $$'", "exit"},
    752     {progname .. ' -e " "', "ok"},
    753     {progname .. ' -e "os.exit(0, true)"', "ok"},
    754     {progname .. ' -e "os.exit(20, true)"', "exit", 20},
    755   }
    756   print("\n(some error messages are expected now)")
    757   for _, v in ipairs(tests) do
    758     local x, y, z = io.popen(v[1]):close()
    759     local x1, y1, z1 = os.execute(v[1])
    760     assert(x == x1 and y == y1 and z == z1)
    761     if v[2] == "ok" then
    762       assert(x and y == 'exit' and z == 0)
    763     else
    764       assert(not x and y == v[2])   -- correct status and 'what'
    765       -- correct code if known (but always different from 0)
    766       assert((v[3] == nil and z > 0) or v[3] == z)
    767     end
    768   end
    769   print("(done)")
    770 end
    771 
    772 
    773 -- testing tmpfile
    774 f = io.tmpfile()
    775 assert(io.type(f) == "file")
    776 f:write("alo")
    777 f:seek("set")
    778 assert(f:read"a" == "alo")
    779 
    780 end --}
    781 
    782 print'+'
    783 
    784 print("testing date/time")
    785 
    786 assert(os.date("") == "")
    787 assert(os.date("!") == "")
    788 assert(os.date("\0\0") == "\0\0")
    789 assert(os.date("!\0\0") == "\0\0")
    790 local x = string.rep("a", 10000)
    791 assert(os.date(x) == x)
    792 local t = os.time()
    793 D = os.date("*t", t)
    794 assert(os.date(string.rep("%d", 1000), t) ==
    795        string.rep(os.date("%d", t), 1000))
    796 assert(os.date(string.rep("%", 200)) == string.rep("%", 100))
    797 
    798 local function checkDateTable (t)
    799   _G.D = os.date("*t", t)
    800   assert(os.time(D) == t)
    801   load(os.date([[assert(D.year==%Y and D.month==%m and D.day==%d and
    802     D.hour==%H and D.min==%M and D.sec==%S and
    803     D.wday==%w+1 and D.yday==%j)]], t))()
    804   _G.D = nil
    805 end
    806 
    807 checkDateTable(os.time())
    808 if not _port then
    809   -- assume that time_t can represent these values
    810   checkDateTable(0)
    811   checkDateTable(1)
    812   checkDateTable(1000)
    813   checkDateTable(0x7fffffff)
    814   checkDateTable(0x80000000)
    815 end
    816 
    817 checkerr("invalid conversion specifier", os.date, "%")
    818 checkerr("invalid conversion specifier", os.date, "%9")
    819 checkerr("invalid conversion specifier", os.date, "%")
    820 checkerr("invalid conversion specifier", os.date, "%O")
    821 checkerr("invalid conversion specifier", os.date, "%E")
    822 checkerr("invalid conversion specifier", os.date, "%Ea")
    823 
    824 checkerr("not an integer", os.time, {year=1000, month=1, day=1, hour='x'})
    825 checkerr("not an integer", os.time, {year=1000, month=1, day=1, hour=1.5})
    826 
    827 checkerr("missing", os.time, {hour = 12})   -- missing date
    828 
    829 
    830 if string.packsize("i") == 4 then   -- 4-byte ints
    831   checkerr("field 'year' is out-of-bound", os.time,
    832               {year = -(1 << 31) + 1899, month = 1, day = 1})
    833 
    834   checkerr("field 'year' is out-of-bound", os.time,
    835               {year = -(1 << 31), month = 1, day = 1})
    836 
    837   if math.maxinteger > 2^31 then   -- larger lua_integer?
    838     checkerr("field 'year' is out-of-bound", os.time,
    839                 {year = (1 << 31) + 1900, month = 1, day = 1})
    840   end
    841 end
    842 
    843 
    844 if not _port then
    845   -- test Posix-specific modifiers
    846   assert(type(os.date("%Ex")) == 'string')
    847   assert(type(os.date("%Oy")) == 'string')
    848 
    849   -- test large dates (assume at least 4-byte ints and time_t)
    850   local t0 = os.time{year = 1970, month = 1, day = 0}
    851   local t1 = os.time{year = 1970, month = 1, day = 0, sec = (1 << 31) - 1}
    852   assert(t1 - t0 == (1 << 31) - 1)
    853   t0 = os.time{year = 1970, month = 1, day = 1}
    854   t1 = os.time{year = 1970, month = 1, day = 1, sec = -(1 << 31)}
    855   assert(t1 - t0 == -(1 << 31))
    856 
    857   -- test out-of-range dates (at least for Unix)
    858   if maxint >= 2^62 then  -- cannot do these tests in Small Lua
    859     -- no arith overflows
    860     checkerr("out-of-bound", os.time, {year = -maxint, month = 1, day = 1})
    861     if string.packsize("i") == 4 then   -- 4-byte ints
    862       if testerr("out-of-bound", os.date, "%Y", 2^40) then
    863         -- time_t has 4 bytes and therefore cannot represent year 4000
    864         print("  4-byte time_t")
    865         checkerr("cannot be represented", os.time, {year=4000, month=1, day=1})
    866       else
    867         -- time_t has 8 bytes; an int year cannot represent a huge time
    868         print("  8-byte time_t")
    869         checkerr("cannot be represented", os.date, "%Y", 2^60)
    870 
    871         -- this is the maximum year
    872         assert(tonumber(os.time
    873           {year=(1 << 31) + 1899, month=12, day=31, hour=23, min=59, sec=59}))
    874 
    875         -- this is too much
    876         checkerr("represented", os.time,
    877           {year=(1 << 31) + 1899, month=12, day=31, hour=23, min=59, sec=60})
    878       end
    879 
    880       -- internal 'int' fields cannot hold these values
    881       checkerr("field 'day' is out-of-bound", os.time,
    882                   {year = 0, month = 1, day = 2^32})
    883 
    884       checkerr("field 'month' is out-of-bound", os.time,
    885                   {year = 0, month = -((1 << 31) + 1), day = 1})
    886 
    887       checkerr("field 'year' is out-of-bound", os.time,
    888                   {year = (1 << 31) + 1900, month = 1, day = 1})
    889 
    890     else    -- 8-byte ints
    891       -- assume time_t has 8 bytes too
    892       print("  8-byte time_t")
    893       assert(tonumber(os.date("%Y", 2^60)))
    894 
    895       -- but still cannot represent a huge year
    896       checkerr("cannot be represented", os.time, {year=2^60, month=1, day=1})
    897     end
    898   end
    899 end
    900 
    901 do
    902   local D = os.date("*t")
    903   local t = os.time(D)
    904   if D.isdst == nil then
    905     print("no daylight saving information")
    906   else
    907     assert(type(D.isdst) == 'boolean')
    908   end
    909   D.isdst = nil
    910   local t1 = os.time(D)
    911   assert(t == t1)   -- if isdst is absent uses correct default
    912 end
    913 
    914 local D = os.date("*t")
    915 t = os.time(D)
    916 D.year = D.year-1;
    917 local t1 = os.time(D)
    918 -- allow for leap years
    919 assert(math.abs(os.difftime(t,t1)/(24*3600) - 365) < 2)
    920 
    921 -- should not take more than 1 second to execute these two lines
    922 t = os.time()
    923 t1 = os.time(os.date("*t"))
    924 local diff = os.difftime(t1,t)
    925 assert(0 <= diff and diff <= 1)
    926 diff = os.difftime(t,t1)
    927 assert(-1 <= diff and diff <= 0)
    928 
    929 local t1 = os.time{year=2000, month=10, day=1, hour=23, min=12}
    930 local t2 = os.time{year=2000, month=10, day=1, hour=23, min=10, sec=19}
    931 assert(os.difftime(t1,t2) == 60*2-19)
    932 
    933 -- since 5.3.3, 'os.time' normalizes table fields
    934 t1 = {year = 2005, month = 1, day = 1, hour = 1, min = 0, sec = -3602}
    935 os.time(t1)
    936 assert(t1.day == 31 and t1.month == 12 and t1.year == 2004 and
    937        t1.hour == 23 and t1.min == 59 and t1.sec == 58 and
    938        t1.yday == 366)
    939 
    940 io.output(io.stdout)
    941 local t = os.date('%d %m %Y %H %M %S')
    942 local d, m, a, h, min, s = string.match(t,
    943                              "(%d+) (%d+) (%d+) (%d+) (%d+) (%d+)")
    944 d = tonumber(d)
    945 m = tonumber(m)
    946 a = tonumber(a)
    947 h = tonumber(h)
    948 min = tonumber(min)
    949 s = tonumber(s)
    950 io.write(string.format('test done on %2.2d/%2.2d/%d', d, m, a))
    951 io.write(string.format(', at %2.2d:%2.2d:%2.2d\n', h, min, s))
    952 io.write(string.format('%s\n', _VERSION))
    953 
    954