github.com/hirochachacha/plua@v0.0.0-20170217012138-c82f520cc725/testdata/lua-5.3.3-tests/files.lua (about)

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