--[[ luaposix on-target payload for 41-luaposix.test. Prints tagged "R
- " lines that the .test greps and feeds to check_eq, so a wrong ANSWER is a named case rather than a non-zero exit. Every call is wrapped: on this arm several of these bindings reach syscalls the wasm32 kernel does not implement, and one of them raising must not take the other twenty-seven cases with it. Argument 1 is a writable scratch directory the .test has already staged, so the filesystem answers can be compared against what the shell put there. ]] local scratch = ... or '/tmp' local function emit(tag, value) io.write('R', tag, ' ', tostring(value), '\n') end -- Report a call as one value. A raised error and a nil-return are different -- findings and must not collapse into each other: luaposix signals a failed -- syscall by returning nil,msg,errnum, so a RAISE means the binding itself broke. local function try(tag, fn, ...) local ok, a, b, c = pcall(fn, ...) if not ok then emit(tag, 'RAISED:' .. tostring(a):gsub('%s+', '_')) return nil end if a == nil then emit(tag, 'FAIL:' .. tostring(c) .. ':' .. tostring(b):gsub('%s+', '_')) return nil, b, c end return a, b, c end --=== R0: the port's own claim -- no dlopen anywhere ========================== -- Every module reached package.preload at startup; none of them is a .so, and -- there is no .so on this image to find. These cases are the ones that go red if -- the static-registration model is ever quietly dropped. local MODULES = { 'posix.ctype', 'posix.dirent', 'posix.errno', 'posix.fcntl', 'posix.fnmatch', 'posix.glob', 'posix.grp', 'posix.libgen', 'posix.poll', 'posix.pwd', 'posix.sched', 'posix.signal', 'posix.stdio', 'posix.stdlib', 'posix.sys.msg', 'posix.sys.resource', 'posix.sys.socket', 'posix.sys.stat', 'posix.sys.statvfs', 'posix.sys.time', 'posix.sys.times', 'posix.sys.utsname', 'posix.sys.wait', 'posix.syslog', 'posix.termio', 'posix.time', 'posix.unistd', 'posix.utime', } local npreload, nrequire, missing = 0, 0, {} for _, m in ipairs(MODULES) do if package.preload[m] ~= nil then npreload = npreload + 1 end local ok, mod = pcall(require, m) if ok and type(mod) == 'table' then nrequire = nrequire + 1 else missing[#missing + 1] = m end end emit('0-modules', #MODULES) emit('0-preload', npreload) emit('0-require', nrequire) emit('0-missing', #missing == 0 and 'none' or table.concat(missing, ',')) -- The C-module search path finds nothing for these names, which is what makes -- "it was the preload searcher" a measurement rather than an assumption. emit('0-cpath-unistd', tostring(package.searchpath('posix.unistd', package.cpath))) -- ...and the aggregate module is a plain .lua off package.path, so the two halves -- of the package arrive by two different routes and both are asserted. emit('0-path-init', package.searchpath('posix', package.path) ~= nil and 'found' or 'nil') emit('0-version', (pcall(require, 'posix.version')) and require('posix.version') or 'ERR') local unistd = require 'posix.unistd' local errno = require 'posix.errno' local sysstat = require 'posix.sys.stat' --=== R1: identity -- getpid is checked against $$ by the .test =============== emit('1-getpid', unistd.getpid()) emit('1-getppid', unistd.getppid()) emit('1-getuid', unistd.getuid()) emit('1-geteuid', unistd.geteuid()) emit('1-getgid', unistd.getgid()) emit('1-getegid', unistd.getegid()) --=== R2: stat against a file the shell staged ================================ local probe = scratch .. '/stat-probe' local st = try('2-stat-raised', sysstat.stat, probe) if st then emit('2-size', st.st_size) emit('2-mode-perm', string.format('%o', st.st_mode % 512)) emit('2-isreg', sysstat.S_ISREG(st.st_mode) ~= 0 and 'yes' or 'no') emit('2-isdir', sysstat.S_ISDIR(st.st_mode) ~= 0 and 'yes' or 'no') emit('2-nlink', st.st_nlink) emit('2-uid', st.st_uid) end local dst = try('2-statdir-raised', sysstat.stat, scratch) if dst then emit('2-dir-isdir', sysstat.S_ISDIR(dst.st_mode) ~= 0 and 'yes' or 'no') emit('2-dir-isreg', sysstat.S_ISREG(dst.st_mode) ~= 0 and 'yes' or 'no') end --=== R3: errno on a deliberate failure ======================================= -- The interesting half is not that it failed; it is that the NUMBER is right and -- that errno was left readable afterwards. local ENOENT = errno.ENOENT emit('3-enoent-const', ENOENT) local r, msg, num = sysstat.stat(scratch .. '/definitely-not-here') emit('3-stat-ret', r == nil and 'nil' or 'NOT-NIL') emit('3-stat-errnum', num) -- luaposix prefixes the failing path onto strerror's text, so the assertion is -- containment: the number and the text have to agree, and the message has to say -- which path it was about. emit('3-stat-msg', tostring(msg)) emit('3-stat-msg-has-strerror', (msg and num and msg:find((errno.errno(num)), 1, true)) and 'yes' or 'no') emit('3-stat-msg-has-path', (msg and msg:find('definitely-not-here', 1, true)) and 'yes' or 'no') emit('3-errno-text', tostring((errno.errno(ENOENT)))) -- errno(3) is settable, and round-tripping it proves the binding reads the real -- errno rather than a number it cached at load time. errno.set_errno(errno.EPERM) local setmsg, setnum = errno.errno() emit('3-set-errno-num', tostring(setnum)) emit('3-set-errno-msg-matches', (setmsg == (errno.errno(errno.EPERM))) and 'yes' or 'no') local _, _, an = unistd.access(scratch .. '/definitely-not-here', 'f') emit('3-access-errnum', tostring(an)) --=== R4: a pipe, written and read back ======================================= local rfd, wfd = try('4-pipe-raised', unistd.pipe) if rfd then local payload = 'luaposix-pipe-0123456789' local nw = unistd.write(wfd, payload) emit('4-written', tostring(nw)) unistd.close(wfd) local got = unistd.read(rfd, 128) emit('4-read', tostring(got)) emit('4-roundtrip', got == payload and 'yes' or 'no') -- EOF after the writer closed is a distinct answer from a short read. local eof = unistd.read(rfd, 8) emit('4-eof', (eof == '' or eof == nil) and 'eof' or ('MORE:' .. tostring(eof))) unistd.close(rfd) end --=== R5: dup2/fileno plumbing on a real file ================================= local fcntl = require 'posix.fcntl' local fd = try('5-open-raised', fcntl.open, probe, fcntl.O_RDONLY) if fd then local head = unistd.read(fd, 5) emit('5-read5', tostring(head)) local dup = unistd.dup(fd) emit('5-dup-distinct', (dup and dup ~= fd) and 'yes' or 'no') if dup then unistd.close(dup) end unistd.close(fd) end emit('5-O_RDONLY', fcntl.O_RDONLY) emit('5-O_WRONLY', fcntl.O_WRONLY) emit('5-O_CREAT-set', (fcntl.O_CREAT ~= nil and fcntl.O_CREAT ~= 0) and 'yes' or 'no') --=== R6: strings and paths -- answers the shell has its own tools for ======== local libgen = require 'posix.libgen' emit('6-basename', libgen.basename('/usr/local/lib/liblua.a')) emit('6-dirname', libgen.dirname('/usr/local/lib/liblua.a')) local fnmatch = require 'posix.fnmatch' emit('6-fnmatch-hit', fnmatch.fnmatch('*.lua', 'init.lua') == 0 and 'yes' or 'no') emit('6-fnmatch-miss', fnmatch.fnmatch('*.lua', 'init.c') ~= 0 and 'yes' or 'no') -- posix.ctype binds exactly two of : isgraph and isprint. The rest of -- the family is not missing here, it was never bound upstream (ext/posix/ctype.c -- registers those two and nothing else). -- -- These return an INTEGER, not a boolean, and glibc's isgraph yields the class -- bit rather than 1 -- so the answer is normalised on ~= 0 here. Testing the -- returned value for truth instead would report every character as graphic, -- because 0 is true in Lua. local ctype = require 'posix.ctype' local function ctype_yes(v) return (type(v) == 'number' and v ~= 0) and 'yes' or 'no' end emit('6-isgraph-digit', ctype_yes(ctype.isgraph('7'))) emit('6-isgraph-space', ctype_yes(ctype.isgraph(' '))) emit('6-isprint-space', ctype_yes(ctype.isprint(' '))) emit('6-isprint-tab', ctype_yes(ctype.isprint('\t'))) emit('6-ctype-exports', (function() local n = {} for k in pairs(ctype) do if type(ctype[k]) == 'function' then n[#n+1] = k end end table.sort(n); return table.concat(n, ',') end)()) --=== R7: the directory, globbed and read ==================================== -- A directory of its OWN, holding only what the .test put there. The scratch root -- also collects this run's stdout and pid files, so counting it would compare -- against a number the shell took before those existed. local tree = scratch .. '/tree' local dirent = require 'posix.dirent' local names = try('7-dir-raised', dirent.dir, tree) if names then local n = 0 for _, e in ipairs(names) do if e ~= '.' and e ~= '..' then n = n + 1 end end emit('7-dir-count', n) end -- glob(3)'s flags argument is required by the binding, not optional. local glob = require 'posix.glob' local g = glob.glob(tree .. '/glob-*', 0) emit('7-glob-count', g and #g or 'nil') emit('7-glob-first', (g and g[1]) and g[1]:gsub('.*/', '') or 'nil') --=== R8: uname, users, groups -- all confirmable from the shell ============= local uname = require 'posix.sys.utsname' local u = try('8-uname-raised', uname.uname) if u then emit('8-sysname', u.sysname) emit('8-machine', u.machine) emit('8-release', u.release) end local pwd = require 'posix.pwd' local pw = try('8-getpwuid-raised', pwd.getpwuid, 0) if pw then emit('8-root-name', pw.pw_name) emit('8-root-uid', pw.pw_uid) emit('8-root-dir', pw.pw_dir) end local grp = require 'posix.grp' local gr = try('8-getgrgid-raised', grp.getgrgid, 0) if gr then emit('8-gid0-name', gr.gr_name) end --=== R9: time ================================================================ local ptime = require 'posix.time' local now = os.time() local tm = try('9-gmtime-raised', ptime.gmtime, now) if tm then emit('9-gmtime-year', tm.tm_year + 1900) emit('9-strftime', ptime.strftime('%Y-%m-%d', tm)) emit('9-mktime-roundtrip', (ptime.mktime(ptime.localtime(now)) == now) and 'yes' or 'no') end -- CLOCK_REALTIME is seeded from the browser host's clock here, so the assertion is -- agreement with os.time() rather than an absolute value. local ct = try('9-clock-raised', ptime.clock_gettime, ptime.CLOCK_REALTIME) if ct then local skew = math.abs(ct.tv_sec - now) emit('9-clock-agrees', skew <= 2 and 'yes' or ('SKEW:' .. skew)) end local times = require 'posix.sys.times' local t = try('9-times-raised', times.times) if t then emit('9-times-has-utime', t.tms_utime ~= nil and 'yes' or 'no') end --=== R10: filesystem statistics ============================================= local statvfs = require 'posix.sys.statvfs' local vfs = try('10-statvfs-raised', statvfs.statvfs, '/') if vfs then emit('10-bsize-positive', (vfs.f_bsize and vfs.f_bsize > 0) and 'yes' or ('BAD:' .. tostring(vfs.f_bsize))) emit('10-blocks-present', vfs.f_blocks ~= nil and 'yes' or 'no') end --=== R11: poll on the pipe ================================================== local poll = require 'posix.poll' local pr, pw2 = try('11-pipe-raised', unistd.pipe) if pr then local fds = {[pr] = {events = {IN = true}}} emit('11-poll-empty', tostring(poll.poll(fds, 0))) unistd.write(pw2, 'x') local n = poll.poll(fds, 0) emit('11-poll-ready', tostring(n)) emit('11-poll-revents-in', (fds[pr].revents and fds[pr].revents.IN) and 'yes' or 'no') unistd.close(pr); unistd.close(pw2) end --=== R12: the platform edges -- expected to be where the reds are =========== -- Each of these is a real binding over a syscall this kernel may not implement. -- They are driven so the gap is NAMED, not so the suite looks green. -- one call each: calling twice to read the value and then the errno would be two -- different syscalls, and on a flaky edge they could disagree. local function edge(tag, fn, ...) local ok, a, b, c = pcall(fn, ...) if not ok then emit(tag, 'RAISED:' .. tostring(a):gsub('%s+', '_')) elseif a == nil then emit(tag, 'FAIL:' .. tostring(c)) else emit(tag, tostring(a)) end return ok and a or nil end local sched = require 'posix.sched' edge('12-sched-getscheduler', sched.sched_getscheduler) local msg = require 'posix.sys.msg' edge('12-sysv-msgget', msg.msgget, msg.IPC_PRIVATE or 0, 0) local sock = require 'posix.sys.socket' local sfd, serr, snum = sock.socket(sock.AF_UNIX, sock.SOCK_STREAM, 0) emit('12-socket-unix', sfd and 'ok' or ('FAIL:' .. tostring(snum))) if sfd then unistd.close(sfd) end -- tcgetattr is asked about fd 0, whose tty-ness is a property of the harness and -- not of luaposix. The .test compares this against the SHELL's own `[ -t 0 ]` -- verdict, so a green here means "the binding agrees with the shell about what -- fd 0 is" -- which is the only claim this suite can honestly make about it. local termio = require 'posix.termio' local tc, _tcerr, tcnum = termio.tcgetattr(0) emit('12-tcgetattr-stdin', tc and 'ok' or ('FAIL:' .. tostring(tcnum))) emit('12-crypt-absent', unistd.crypt == nil and 'yes' or 'NO-crypt-is-present') local wait = require 'posix.sys.wait' emit('12-wait-has-wait', type(wait.wait) == 'function' and 'yes' or 'no') -- fork(2) is the one call here that could take the rest of the run with it if the -- binding raised rather than returned, so it is the one that is pcall'd by hand. local forkok, forkpid, _forkerr, forknum = pcall(unistd.fork) if not forkok then emit('12-fork', 'RAISED:' .. tostring(forkpid):gsub('%s+', '_')) emit('12-fork-exit', 'n/a') forkpid = nil elseif forkpid == 0 then -- Child, if this platform ever gets one. Leave without touching the parent's -- stdio buffers, or every line above is printed twice. unistd._exit(7) elseif forkpid then local _wpid, _wstatus, wcode = wait.wait(forkpid) emit('12-fork', 'ok') emit('12-fork-exit', tostring(wcode)) else emit('12-fork', 'FAIL:' .. tostring(forknum)) emit('12-fork-exit', 'n/a') end --=== R13: the aggregate module ============================================== -- posix/init.lua pulls ~20 of the C modules together at load time, so it is the -- single strongest check that the whole preload set is coherent. local ok, posix = pcall(require, 'posix') emit('13-require-posix', ok and 'ok' or ('ERR:' .. tostring(posix):gsub('%s+', '_'))) if ok then emit('13-getpid-agrees', posix.getpid and 'has-getpid' or 'no-getpid') emit('13-basename', tostring(posix.basename and posix.basename('/a/b/c') or 'nil')) end io.write('ALL-LUAPOSIX-DONE\n')