329 lines
10 KiB
Lua
329 lines
10 KiB
Lua
--- Tests the io library is (mostly) consistent with PUC Lua.
|
||
--
|
||
-- These tests are based on the tests for Lua 5.1 and 5.3
|
||
|
||
describe("The io library", function()
|
||
local file = "/test-files/tmp.txt"
|
||
local otherfile = "/test-files/tmp2.txt"
|
||
|
||
local t = '0123456789'
|
||
for _ = 1, 12 do t = t .. t end
|
||
assert(#t == 10 * 2 ^ 12)
|
||
|
||
local function read_all(f)
|
||
local h = fs.open(f, "rb")
|
||
local contents = h.readAll()
|
||
h.close()
|
||
return contents
|
||
end
|
||
|
||
local function write_file(f, contents)
|
||
local h = fs.open(f, "wb")
|
||
h.write(contents)
|
||
h.close()
|
||
end
|
||
|
||
local function setup()
|
||
write_file(file, "\"<EFBFBD>lo\"{a}\nsecond line\nthird line \n<EFBFBD>fourth_line\n\n\9\9 3450\n")
|
||
end
|
||
|
||
describe("io.close", function()
|
||
it("cannot close stdin", function()
|
||
expect{ io.stdin:close() }:same { nil, "attempt to close standard stream" }
|
||
end)
|
||
it("cannot close stdout", function()
|
||
expect{ io.stdout:close() }:same { nil, "attempt to close standard stream" }
|
||
end)
|
||
it("cannot close stdout", function()
|
||
expect{ io.stdout:close() }:same { nil, "attempt to close standard stream" }
|
||
end)
|
||
end)
|
||
|
||
it("io.input on a handle returns that handle", function()
|
||
expect(io.input(io.stdin)):equals(io.stdin)
|
||
end)
|
||
|
||
it("io.output on a handle returns that handle", function()
|
||
expect(io.output(io.stdout)):equals(io.stdout)
|
||
end)
|
||
|
||
it("defines a __name field", function()
|
||
expect(getmetatable(io.input()).__name):eq("FILE*")
|
||
end)
|
||
|
||
describe("io.type", function()
|
||
it("returns file on handles", function()
|
||
local handle = io.input()
|
||
expect(handle):type("table")
|
||
expect(io.type(handle)):equals("file")
|
||
expect(io.type(io.stdin)):equals("file")
|
||
end)
|
||
|
||
it("returns nil on values", function()
|
||
expect(io.type(8)):equals(nil)
|
||
end)
|
||
|
||
it("returns nil on tables", function()
|
||
expect(io.type(setmetatable({}, {}))):equals(nil)
|
||
end)
|
||
end)
|
||
|
||
describe("io.lines", function()
|
||
it("validates arguments", function()
|
||
io.lines(nil)
|
||
expect.error(io.lines, ""):eq("/: No such file")
|
||
expect.error(io.lines, false):eq("bad argument #1 (expected string, got boolean)")
|
||
end)
|
||
|
||
it("closes the file", function()
|
||
setup()
|
||
|
||
local n = 0
|
||
local f = io.lines(file)
|
||
while f() do n = n + 1 end
|
||
expect(n):eq(6)
|
||
|
||
expect.error(f):eq("file is already closed")
|
||
expect.error(f):eq("file is already closed")
|
||
end)
|
||
|
||
it("can copy a file", function()
|
||
setup()
|
||
|
||
local n = 0
|
||
io.output(otherfile)
|
||
for l in io.lines(file) do
|
||
io.write(l, "\n")
|
||
n = n + 1
|
||
end
|
||
io.close()
|
||
expect(n):eq(6)
|
||
|
||
io.input(file)
|
||
local f = io.open(otherfile):lines()
|
||
local n = 0
|
||
for l in io.lines() do
|
||
expect(l):eq(f())
|
||
n = n + 1
|
||
end
|
||
expect(n):eq(6)
|
||
end)
|
||
|
||
it("does not close on a normal file handle", function()
|
||
setup()
|
||
|
||
local f = assert(io.open(file))
|
||
local n = 0
|
||
for _ in f:lines() do n = n + 1 end
|
||
expect(n):eq(6)
|
||
|
||
expect(tostring(f):sub(1, 5)):eq("file ")
|
||
assert(f:close())
|
||
|
||
expect(tostring(f)):eq("file (closed)")
|
||
expect(io.type(f)):eq("closed file")
|
||
end)
|
||
|
||
it("accepts multiple arguments", function()
|
||
write_file(file, "0123456789\n")
|
||
for a, b in io.lines(file, 1, 1) do
|
||
if a == "\n" then
|
||
expect(b):eq(nil)
|
||
else
|
||
expect(tonumber(a)):eq(tonumber(b) - 1)
|
||
end
|
||
end
|
||
|
||
for a, b, c in io.lines(file, 1, 2, "a") do
|
||
expect(a):eq("0")
|
||
expect(b):eq("12")
|
||
expect(c):eq("3456789\n")
|
||
end
|
||
|
||
for a, b, c in io.lines(file, "a", 0, 1) do
|
||
if a == "" then break end
|
||
expect(a):eq("0123456789\n")
|
||
expect(b):eq(nil)
|
||
expect(c):eq(nil)
|
||
end
|
||
|
||
write_file(file, "00\n10\n20\n30\n40\n")
|
||
for a, b in io.lines(file, "n", "n") do
|
||
if a == 40 then
|
||
expect(b):eq(nil)
|
||
else
|
||
expect(a):eq(b - 10)
|
||
end
|
||
end
|
||
end)
|
||
end)
|
||
|
||
describe("io.open", function()
|
||
it("validates arguments", function()
|
||
io.open("")
|
||
io.open("", "r")
|
||
|
||
expect.error(io.open, nil):eq("bad argument #1 (expected string, got nil)")
|
||
expect.error(io.open, "", false):eq("bad argument #2 (expected string, got boolean)")
|
||
end)
|
||
|
||
it("checks the mode", function()
|
||
io.open(file, "w"):close()
|
||
|
||
-- This really should be invalid mode, but I'll live.
|
||
expect.error(io.open, file, "rw"):str_match("Unsupported mode")
|
||
-- TODO: expect.error(io.open, file, "rb+"):str_match("Unsupported mode")
|
||
expect.error(io.open, file, "r+bk"):str_match("Unsupported mode")
|
||
expect.error(io.open, file, ""):str_match("Unsupported mode")
|
||
expect.error(io.open, file, "+"):str_match("Unsupported mode")
|
||
expect.error(io.open, file, "b"):str_match("Unsupported mode")
|
||
|
||
assert(io.open(file, "r+b")):close()
|
||
assert(io.open(file, "r+")):close()
|
||
assert(io.open(file, "rb")):close()
|
||
end)
|
||
|
||
it("returns an error message on non-existent files", function()
|
||
local a, b = io.open('xuxu_nao_existe')
|
||
expect(a):equals(nil)
|
||
expect(b):type("string")
|
||
end)
|
||
end)
|
||
|
||
describe("a readable handle", function()
|
||
it("cannot be written to", function()
|
||
write_file(file, "")
|
||
io.input(file)
|
||
expect { io.input():write("xuxu") }:same { nil, "file is not writable" }
|
||
io.input(io.stdin)
|
||
end)
|
||
|
||
it("supports various modes", function()
|
||
write_file(file, "alo\n " .. t .. " ;end of file\n")
|
||
|
||
io.input(file)
|
||
expect(io.read()):eq("alo")
|
||
expect(io.read(1)):eq(' ')
|
||
expect(io.read(#t)):eq(t)
|
||
expect(io.read(1)):eq(' ')
|
||
expect(io.read(0))
|
||
expect(io.read('*a')):eq(';end of file\n')
|
||
expect(io.read(0)):eq(nil)
|
||
expect(io.close(io.input())):eq(true)
|
||
|
||
fs.delete(file)
|
||
end)
|
||
|
||
it("support seeking", function()
|
||
setup()
|
||
io.input(file)
|
||
|
||
expect(io.read(0)):eq("") -- not eof
|
||
expect(io.read(5, '*l')):eq('"<22>lo"')
|
||
expect(io.read(0)):eq("")
|
||
expect(io.read()):eq("second line")
|
||
local x = io.input():seek()
|
||
expect(io.read()):eq("third line ")
|
||
assert(io.input():seek("set", x))
|
||
expect(io.read('*l')):eq("third line ")
|
||
expect(io.read(1)):eq("<EFBFBD>")
|
||
expect(io.read(#"fourth_line")):eq("fourth_line")
|
||
assert(io.input():seek("cur", -#"fourth_line"))
|
||
expect(io.read()):eq("fourth_line")
|
||
expect(io.read()):eq("") -- empty line
|
||
expect(io.read(8)):eq('\9\9 3450') -- FIXME: Not actually supported
|
||
expect(io.read(1)):eq('\n')
|
||
expect(io.read(0)):eq(nil) -- end of file
|
||
expect(io.read(1)):eq(nil) -- end of file
|
||
expect(({ io.read(1) })[2]):eq(nil)
|
||
expect(io.read()):eq(nil) -- end of file
|
||
expect(({ io.read() })[2]):eq(nil)
|
||
expect(io.read('*n')):eq(nil) -- end of file
|
||
expect(({ io.read('*n') })[2]):eq(nil)
|
||
expect(io.read('*a')):eq('') -- end of file (OK for `*a')
|
||
expect(io.read('*a')):eq('') -- end of file (OK for `*a')
|
||
|
||
io.close(io.input())
|
||
end)
|
||
|
||
it("supports the 'L' mode", function()
|
||
write_file(file, "\n\nline\nother")
|
||
|
||
io.input(file)
|
||
expect(io.read"L"):eq("\n")
|
||
expect(io.read"L"):eq("\n")
|
||
expect(io.read"L"):eq("line\n")
|
||
expect(io.read"L"):eq("other")
|
||
expect(io.read"L"):eq(nil)
|
||
io.input():close()
|
||
|
||
local f = assert(io.open(file))
|
||
local s = ""
|
||
for l in f:lines("L") do s = s .. l end
|
||
expect(s):eq("\n\nline\nother")
|
||
f:close()
|
||
|
||
io.input(file)
|
||
s = ""
|
||
for l in io.lines(nil, "L") do s = s .. l end
|
||
expect(s):eq("\n\nline\nother")
|
||
io.input():close()
|
||
|
||
s = ""
|
||
for l in io.lines(file, "L") do s = s .. l end
|
||
expect(s):eq("\n\nline\nother")
|
||
|
||
s = ""
|
||
for l in io.lines(file, "l") do s = s .. l end
|
||
expect(s):eq("lineother")
|
||
|
||
write_file(file, "a = 10 + 34\na = 2*a\na = -a\n")
|
||
local t = {}
|
||
load(io.lines(file, "L"), nil, nil, t)()
|
||
expect(t.a):eq(-((10 + 34) * 2))
|
||
end)
|
||
end)
|
||
|
||
describe("a writable handle", function()
|
||
it("supports seeking", function()
|
||
fs.delete(file)
|
||
io.output(file)
|
||
|
||
expect(io.output()):not_equals(io.stdout)
|
||
|
||
expect(io.output():seek()):equal(0)
|
||
assert(io.write("alo alo"))
|
||
expect(io.output():seek()):equal(#"alo alo")
|
||
expect(io.output():seek("cur", -3)):equal(#"alo alo" - 3)
|
||
assert(io.write("joao"))
|
||
expect(io.output():seek("end")):equal(#"alo joao")
|
||
|
||
expect(io.output():seek("set")):equal(0)
|
||
|
||
assert(io.write('"<22>lo"', "{a}\n", "second line\n", "third line \n"))
|
||
assert(io.write('<EFBFBD>fourth_line'))
|
||
|
||
io.output(io.stdout)
|
||
expect(io.output()):equals(io.stdout)
|
||
end)
|
||
|
||
it("supports appending", function()
|
||
io.output(file)
|
||
io.write("alo\n")
|
||
io.close()
|
||
expect.error(io.write)
|
||
|
||
local f = io.open(file, "a")
|
||
io.output(f)
|
||
|
||
assert(io.write(' ' .. t .. ' '))
|
||
assert(io.write(';', 'end of file\n'))
|
||
f:flush()
|
||
io.flush()
|
||
f:close()
|
||
|
||
expect(read_all(file)):eq("alo\n " .. t .. " ;end of file\n")
|
||
end)
|
||
end)
|
||
end)
|