1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-12-12 11:10:29 +00:00

Copy across a bunch of 5.1/5.3 io tests

I've been meaning to do this for ages. Woops.
This commit is contained in:
SquidDev 2020-05-11 18:05:23 +01:00
parent 156023b154
commit 9a71dc1a26
2 changed files with 277 additions and 21 deletions

View File

@ -75,7 +75,10 @@ handleMetatable = {
if not handle.read then return nil, "file is not readable" end if not handle.read then return nil, "file is not readable" end
local args = table.pack(...) local args = table.pack(...)
return function() return checkResult(self, self:read(table.unpack(args, 1, args.n))) end return function()
if self._closed then error("file is already closed", 2) end
return checkResult(self, self:read(table.unpack(args, 1, args.n)))
end
end, end,
read = function(self, ...) read = function(self, ...)
@ -259,12 +262,13 @@ end
-- instead. In this case, the handle is not used. -- instead. In this case, the handle is not used.
-- --
-- @tparam[opt] string filename The name of the file to extract lines from -- @tparam[opt] string filename The name of the file to extract lines from
-- @param ... The argument to pass to @{Handle:read} for each line.
-- @treturn function():string|nil The line iterator. -- @treturn function():string|nil The line iterator.
-- @throws If the file cannot be opened for reading -- @throws If the file cannot be opened for reading
-- --
-- @see Handle:lines -- @see Handle:lines
-- @see io.input -- @see io.input
function lines(filename) function lines(filename, ...)
expect(1, filename, "string", "nil") expect(1, filename, "string", "nil")
if filename then if filename then
local ok, err = open(filename, "rb") local ok, err = open(filename, "rb")
@ -273,9 +277,9 @@ function lines(filename)
-- We set this magic flag to mark this file as being opened by io.lines and so should be -- We set this magic flag to mark this file as being opened by io.lines and so should be
-- closed automatically -- closed automatically
ok._autoclose = true ok._autoclose = true
return ok:lines() return ok:lines(...)
else else
return currentInput:lines() return currentInput:lines(...)
end end
end end
@ -313,7 +317,7 @@ end
-- @throws If the provided filename cannot be opened for writing. -- @throws If the provided filename cannot be opened for writing.
function output(file) function output(file)
if type_of(file) == "string" then if type_of(file) == "string" then
local res, err = open(file, "w") local res, err = open(file, "wb")
if not res then error(err, 2) end if not res then error(err, 2) end
currentOutput = res currentOutput = res
elseif type_of(file) == "table" and getmetatable(file) == handleMetatable then elseif type_of(file) == "table" and getmetatable(file) == handleMetatable then

View File

@ -1,8 +1,44 @@
--- Tests the io library is (mostly) consistent with PUC Lua. --- Tests the io library is (mostly) consistent with PUC Lua.
-- --
-- These tests are based on the tests for Lua 5.1 -- These tests are based on the tests for Lua 5.1 and 5.3
describe("The io library", function() 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() it("io.input on a handle returns that handle", function()
expect(io.input(io.stdin)):equals(io.stdin) expect(io.input(io.stdin)):equals(io.stdin)
end) end)
@ -11,11 +47,16 @@ describe("The io library", function()
expect(io.output(io.stdout)):equals(io.stdout) expect(io.output(io.stdout)):equals(io.stdout)
end) end)
it("defines a __name field", function()
expect(getmetatable(io.input()).__name):eq("FILE*")
end)
describe("io.type", function() describe("io.type", function()
it("returns file on handles", function() it("returns file on handles", function()
local handle = io.input() local handle = io.input()
expect(handle):type("table") expect(handle):type("table")
expect(io.type(handle)):equals("file") expect(io.type(handle)):equals("file")
expect(io.type(io.stdin)):equals("file")
end) end)
it("returns nil on values", function() it("returns nil on values", function()
@ -33,6 +74,88 @@ describe("The io library", function()
expect.error(io.lines, ""):eq("/: No such file") expect.error(io.lines, ""):eq("/: No such file")
expect.error(io.lines, false):eq("bad argument #1 (expected string, got boolean)") expect.error(io.lines, false):eq("bad argument #1 (expected string, got boolean)")
end) 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) end)
describe("io.open", function() describe("io.open", function()
@ -44,6 +167,22 @@ describe("The io library", function()
expect.error(io.open, "", false):eq("bad argument #2 (expected string, got boolean)") expect.error(io.open, "", false):eq("bad argument #2 (expected string, got boolean)")
end) 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() it("returns an error message on non-existent files", function()
local a, b = io.open('xuxu_nao_existe') local a, b = io.open('xuxu_nao_existe')
expect(a):equals(nil) expect(a):equals(nil)
@ -51,26 +190,139 @@ describe("The io library", function()
end) end)
end) end)
pending("io.output allows redirecting and seeking", function() describe("a readable handle", function()
fs.delete("/tmp/io_spec.txt") 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)
io.output("/tmp/io_spec.txt") it("supports various modes", function()
write_file(file, "alo\n " .. t .. " ;end of file\n")
expect(io.output()):not_equals(io.stdout) 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)
expect(io.output():seek()):equal(0) fs.delete(file)
assert(io.write("alo alo")) end)
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) it("support seeking", function()
setup()
io.input(file)
assert(io.write('"<22>lo"', "{a}\n", "second line\n", "third line \n")) expect(io.read(0)):eq("") -- not eof
assert(io.write('<EFBFBD>fourth_line')) 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.output(io.stdout) io.close(io.input())
expect(io.output()):equals(io.stdout) 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)
end) end)