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