-- common testing environment require("posix") -- escape codes to colorize output on terminal local c1="\027[47;34m" local c0="\027[0m" --- -- writes colorized -- function cwriteln(...) io.write(c1, ...) io.write(c0, "\n") end ----- -- initializes the pseudo random generator -- if environemnt "SEED" is set, use that as seed. local seed = os.getenv("SEED") or os.time() math.randomseed(seed) cwriteln("random seed: ", seed) ----- -- creates a tmp directory -- -- @returns the name of the directory -- function mktempd() local f = io.popen('mktemp -td ltest.XXX', 'r') local s = f:read('*a') f:close() s = s:gsub('[\n\r]+', ' ') s = s:match("^%s*(.-)%s*$") return s end ----- -- creates a tmp directory with the -- typical lsyncd test architecture -- -- @returns path of tmpdir -- path of srcdir -- path of trgdir -- function mktemps() local tdir = mktempd().."/" cwriteln("using ", tdir, " as test root") local srcdir = tdir.."src/" local trgdir = tdir.."trg/" posix.mkdir(srcdir) posix.mkdir(trgdir) return tdir, srcdir, trgdir end ---- -- Writes a file with 'text' in it. -- and adds a newline. -- function writefile(filename, text) local f = io.open(filename, "w") if not f then cwriteln("Cannot open '"..filename.."' for writing.") return false end f:write(text) f:write('\n') f:close() return true end ----- -- spawns a subprocess. -- -- @returns the processes pid -- function spawn(...) args = {...} cwriteln("spawning: ", table.concat(args, " ")) local pid = posix.fork() if pid < 0 then cwriteln("Error, failed fork!") os.exit(-1) end if pid == 0 then posix.exec(...) -- should not return cwriteln("Error, failed to spawn: ", ...) os.exit(-1); end return pid end ----- -- Makes a lot of random data -- -- @param rootdir ... the directory to make data in -- @param n ... roughly how much data action will done -- function churn(rootdir, n) -- all dirs created, indexed by integer and path root = {name=""} alldirs = {root} dirsWithFileI = {} dirsWithFileD = {} ----- -- returns the name of a directory -- -- name is internal recursive paramter, keep it nil. -- local function dirname(dir, name) name = name or "" if not dir then return name end return dirname(dir.parent, dir.name .. "/" .. name) end ----- -- Picks a random dir. -- local function pickDir(notRoot) if notRoot then if #alldirs <= 2 then return nil end return alldirs[math.random(2, #alldirs)] end return alldirs[math.random(#alldirs)] end ---- -- Picks a random file. -- -- Returns 3 values: -- * the directory -- * the filename -- * number of files in directory -- local function pickFile() -- picks the random directory if #dirsWithFileI < 1 then return end local rdir = dirsWithFileI[math.random(1, #dirsWithFileI)] if not rdir then return end -- counts the files in there local c = 0 for name, _ in pairs(rdir) do if #name == 2 then c = c + 1 end end -- picks one file at random local cr = math.random(1, c) local fn for name, _ in pairs(rdir) do if #name == 2 then -- filenames are 2 chars wide. cr = cr - 1 if cr == 0 then fn = name break end end end return rdir, fn, c end ----- -- Removes a reference to a file -- -- @param dir --- directory reference -- @param fn --- filename -- @param c --- number of files in dir -- local function rmFileReference(dir, fn, c) dir[fn] = nil if c == 1 then -- if last file from origin dir, it has no files anymore for i, v in ipairs(dirsWithFileI) do if v == dir then table.remove(dirsWithFileI, i) break end end dirsWithFileD[dir] = nil end end ---- -- possible randomized behaviour. -- just gives it a pause -- local function sleep() cwriteln("..zzz..") posix.sleep(1) end ---- -- possible randomized behaviour. -- creates a directory -- local function mkdir() -- chooses a random directory to create it into local rdir = pickDir() -- creates a new random one letter name local nn = string.char(96 + math.random(26)) if not rdir[nn] then local ndir = { name = nn, parent = rdir, } local dn = dirname(ndir) rdir[nn] = dn table.insert(alldirs, ndir) cwriteln("mkdir "..rootdir..dn) posix.mkdir(rootdir..dn) end end ---- -- possible randomized behaviour. -- creates a directory -- local function mkfile() -- chooses a random directory to create it into local rdir = pickDir() -- creates a new random one letter name local nn = 'f'..string.char(96 + math.random(26)) local fn = dirname(rdir) .. nn cwriteln("mkfile "..rootdir..fn) local f = io.open(rootdir..fn, "w") if f then for i=1,10 do f:write(string.char(96 + math.random(26))) end f:write('\n') f:close() rdir[nn]=true if not dirsWithFileD[rdir] then table.insert(dirsWithFileI, rdir) dirsWithFileD[rdir]=true end end end ---- -- possible randomized behaviour. -- moves a directory -- local function mvdir() if #alldirs <= 2 then return end -- chooses a random directory to move local odir = pickDir(true) -- chooses a random directory to move to local tdir = pickDir() -- makes sure tdir is not a subdir of odir local dd = tdir while dd do if odir == dd then return end dd = dd.parent end -- origin name in the target dir already if tdir[odir.name] ~= nil then return end local on = dirname(odir) local tn = dirname(tdir) cwriteln("mvdir ",rootdir,on," -> ",rootdir,tn,odir.name) os.rename(rootdir..on, rootdir..tn..odir.name) odir.parent[odir.name] = nil odir.parent = tdir tdir[odir.name] = odir end ---- -- possible randomized behaviour. -- moves a directory -- local function mvfile() local odir, fn, c = pickFile() if not odir then return end -- picks a directory with a file at random -- picks a target directory at random local tdir = pickDir() local on = dirname(odir) local tn = dirname(tdir) cwriteln("mvfile ",rootdir,on,fn," -> ",rootdir,tn,fn) os.rename(rootdir..on..fn, rootdir..tn..fn) rmFileReference(odir, fn, c) tdir[fn] = true if not dirsWithFileD[tdir] then dirsWithFileD[tdir] = true table.insert(dirsWithFileI, tdir) end end ---- -- possible randomized behaviour. -- moves a directory. -- local function rmfile() local dir, fn, c = pickFile() if dir then local dn = dirname(dir) cwriteln("rmfile ",rootdir,dn,fn) posix.unlink(rootdir..dn..fn) rmFileReference(dir, fn, c) end end local dice = { { 10, sleep }, { 20, mkfile }, { 20, mkdir }, { 20, mvdir }, { 20, rmfile }, } cwriteln("making random data") local ndice = 0 for i, d in ipairs(dice) do ndice = ndice + d[1] d[1] = ndice end for ai=1,n do -- throw a die what to do local acn = math.random(ndice) for i, d in ipairs(dice) do if acn <= d[1] then d[2]() break end end end end