Created
February 21, 2026 13:39
-
-
Save hoelzro/e71211be671f1a593a4d507bc56daacc to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| local inotify = require 'inotify' | |
| local CHARSET = 'abcdefghijklmnopqrstuvwxyz0123456789' | |
| local charset_idx = 0 | |
| local function next_name(len) | |
| charset_idx = charset_idx + 1 | |
| assert(charset_idx <= #CHARSET) | |
| return string.rep(string.sub(CHARSET, charset_idx, charset_idx), len) | |
| end | |
| local function make_tmpdir() | |
| local p = assert(io.popen 'mktemp -d') | |
| local path = p:read '*l' | |
| p:close() | |
| return path | |
| end | |
| local function collect_via_read(handle) | |
| local got = {} | |
| while true do | |
| local events = handle:read() | |
| if not events or #events == 0 then | |
| break | |
| end | |
| for _, ev in ipairs(events) do | |
| if ev.name then | |
| got[ev.name] = true | |
| end | |
| end | |
| end | |
| os.execute 'sleep 1' | |
| return got | |
| end | |
| local function compare_sets(expected, received) | |
| local missing = {} | |
| local extra = {} | |
| for name in pairs(expected) do | |
| if not received[name] then | |
| missing[#missing + 1] = name | |
| end | |
| end | |
| for name in pairs(received) do | |
| if not expected[name] then | |
| extra[#extra + 1] = name | |
| end | |
| end | |
| return missing, extra | |
| end | |
| local function table_count(t) | |
| local count = 0 | |
| for _ in pairs(t) do | |
| count = count + 1 | |
| end | |
| return count | |
| end | |
| local debug = print | |
| -- function debug() end | |
| local function run_round(handle, tmpdir, name_lens) | |
| local expected = {} | |
| for i, len in ipairs(name_lens) do | |
| local name = next_name(len) | |
| expected[name] = true | |
| debug('creating', name) | |
| local f = assert(io.open(tmpdir .. '/' .. name, 'w')) | |
| f:close() | |
| end | |
| -- Events are generated synchronously by the kernel before open() returns, | |
| -- but give the compat layer a moment just in case. | |
| os.execute 'sleep 1' | |
| local got = collect_via_read(handle) | |
| debug('got ', table_count(got), 'events') | |
| debug('expected ', table_count(expected), 'events') | |
| local missing, extra = compare_sets(expected, got) | |
| -- interestingly enough, if I skip this - it works! | |
| for name in pairs(expected) do | |
| os.remove(tmpdir .. '/' .. name) | |
| end | |
| return missing, extra | |
| end | |
| local function report_fail(file_count, missing, extra) | |
| io.stderr:write(string.format( | |
| 'FAIL (files=%d)\n', | |
| file_count | |
| )) | |
| if #missing > 0 then | |
| io.stderr:write(' missing events:\n') | |
| for _, name in ipairs(missing) do | |
| io.stderr:write(string.format(' %s (len=%d)\n', name, #name)) | |
| end | |
| end | |
| if #extra > 0 then | |
| io.stderr:write(' unexpected events:\n') | |
| for _, name in ipairs(extra) do | |
| io.stderr:write(string.format(' %s (len=%d)\n', name, #name)) | |
| end | |
| end | |
| end | |
| local failures = 0 | |
| local function test_round(handle, tmpdir, name_lens) | |
| local missing, extra = run_round(handle, tmpdir, name_lens) | |
| if #missing > 0 or #extra > 0 then | |
| failures = failures + 1 | |
| report_fail(#name_lens, missing, extra) | |
| end | |
| end | |
| local tmpdir = make_tmpdir() | |
| local handle = assert(inotify.init { | |
| blocking = false, | |
| }) | |
| local wd = assert(handle:addwatch(tmpdir, inotify.IN_CREATE)) | |
| for _ = 1, 2 do | |
| local name_lens = {} | |
| for i = 1, 18 do | |
| name_lens[i] = 39 | |
| end | |
| test_round(handle, tmpdir, name_lens) | |
| end | |
| os.exit(failures == 0 and 0 or 1) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment