Skip to content

Commit 6740515

Browse files
committed
tests/lapi: add tests for io functions
The patch adds tests for `io.flush()`, `io.read()`, `io.seek()`, `io.setvbuf()`, `io.write()`. Other functions are not covered.
1 parent b8233dc commit 6740515

File tree

1 file changed

+137
-0
lines changed

1 file changed

+137
-0
lines changed

tests/lapi/io_torture_test.lua

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
--[[
2+
SPDX-License-Identifier: ISC
3+
Copyright (c) 2023-2025, Sergey Bronnikov.
4+
5+
5.7 – Input and Output Facilities
6+
https://www.lua.org/manual/5.1/manual.html#5.7
7+
https://www.lua.org/pil/21.3.html
8+
9+
Synopsis:
10+
11+
io.close([file])
12+
io.flush()
13+
io.open(filename [, mode])
14+
io.read(...)
15+
io.tmpfile()
16+
io.write(...)
17+
file:seek([whence] [, offset])
18+
file:setvbuf(mode [, size])
19+
]]
20+
21+
local luzer = require("luzer")
22+
local test_lib = require("lib")
23+
24+
-- The maximum file size is 1Mb (1000 * 1000).
25+
local MAX_N = 1e3
26+
27+
local function io_seek(self)
28+
local SEEK_MODE = {
29+
"set", -- Base is position 0 (beginning of the file).
30+
"cur", -- Base is current position.
31+
"end", -- Base is end of file.
32+
}
33+
local mode = self.fdp:oneof(SEEK_MODE)
34+
local offset = self.fdp:consume_integer(0, self.MAX_N)
35+
self.fh:seek(mode, offset)
36+
end
37+
38+
local function io_flush(self)
39+
self.fh:flush()
40+
end
41+
42+
local function io_setvbuf(self)
43+
local VBUF_MODE = {
44+
"no", -- No buffering.
45+
"full", -- Full buffering.
46+
"line", -- Line buffering.
47+
}
48+
local mode = self.fdp:oneof(VBUF_MODE)
49+
local size = self.fdp:consume_integer(0, self.MAX_N)
50+
self.fh:setvbuf(mode, size)
51+
end
52+
53+
local function io_write(self)
54+
local str = self.fdp:consume_string(self.MAX_N)
55+
self.fh:write(str)
56+
end
57+
58+
local READ_FORMAT = {
59+
"*n", -- Reads a number.
60+
"*a", -- Reads the whole file, starting at the current
61+
-- position.
62+
"*l", -- Reads the next line (skipping the end of line),
63+
-- returning nil on end of file.
64+
}
65+
if test_lib.lua_current_version_ge_than(5, 2) or
66+
test_lib.lua_version() == "LuaJIT" then
67+
-- "*L" reads the next line keeping the end of line
68+
-- (if present), returning nil on end of file.
69+
table.insert(READ_FORMAT, "*L")
70+
end
71+
72+
local function io_read(self)
73+
local n_formats = self.fdp:consume_integer(1, self.MAX_N)
74+
-- Build a table with formats, which specify what to read. For
75+
-- each format, the function returns a string (or a number)
76+
-- with the characters read, or `nil` if it cannot read data
77+
-- with the specified format. When called without formats, it
78+
-- uses a default format that reads the entire next line.
79+
local read_formats = {}
80+
for _ = 1, n_formats do
81+
local format_is_size = self.fdp:consume_boolean()
82+
if format_is_size then
83+
-- As a special case, `io.read(0)` works as a test for
84+
-- end of file: It returns an empty string if there is
85+
-- more to be read or `nil` otherwise.
86+
local size = self.fdp:consume_integer(0, test_lib.MAX_INT64)
87+
table.insert(read_formats, size)
88+
else
89+
local format = self.fdp:oneof(READ_FORMAT)
90+
table.insert(read_formats, format)
91+
end
92+
end
93+
local _ = self.fh:read(unpack(read_formats))
94+
end
95+
96+
local function io_close(self)
97+
self.fh:close()
98+
end
99+
100+
local io_methods = {
101+
io_flush,
102+
io_read,
103+
io_seek,
104+
io_setvbuf,
105+
io_write,
106+
}
107+
108+
local function io_random_op(self)
109+
local io_method = self.fdp:oneof(io_methods)
110+
io_method(self)
111+
end
112+
113+
local function io_new(fdp)
114+
local fh = io.tmpfile()
115+
return {
116+
close = io_close,
117+
fdp = fdp,
118+
fh = fh,
119+
random_operation = io_random_op,
120+
MAX_N = MAX_N,
121+
}
122+
end
123+
124+
local function TestOneInput(buf)
125+
local fdp = luzer.FuzzedDataProvider(buf)
126+
local nops = fdp:consume_integer(1, MAX_N)
127+
local fh = io_new(fdp)
128+
for _ = 1, nops do
129+
fh:random_operation()
130+
end
131+
fh:close()
132+
end
133+
134+
local args = {
135+
artifact_prefix = "io_torture_",
136+
}
137+
luzer.Fuzz(TestOneInput, nil, args)

0 commit comments

Comments
 (0)