lsyncd/tests/testlib.lua
2015-10-14 14:23:37 +02:00

354 lines
6.9 KiB
Lua

-- common testing environment
posix = 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 file.
--
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 file.
--
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,
-- removes a file.
--
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
-- throws 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