added spec for stringutil, tableutil and the pure part of fsutil

alfons-task
Nathan DECHER 4 years ago
parent 0779ea3ad4
commit 80a5c54032
  1. 2
      Alfons.moon
  2. 76
      moonbuild.lua
  3. 23
      moonbuild/fscache.moon
  4. 24
      moonbuild/fsutil.moon
  5. 20
      moonbuild/stringutil.moon
  6. 3
      moonbuild/tableutil.moon
  7. 111
      spec/fsutil_spec.moon
  8. 78
      spec/stringutil_spec.moon
  9. 82
      spec/tableutil_spec.moon

@ -1,6 +1,8 @@
tasks: tasks:
build: => build: =>
sh "moon bin/moonbuild.moon compile" sh "moon bin/moonbuild.moon compile"
test: =>
sh "busted"
install: => install: =>
sh "moon bin/moonbuild.moon install" sh "moon bin/moonbuild.moon install"
release: => release: =>

@ -28,6 +28,9 @@ makecached = function(fn)
end end
local get local get
get = function(val) get = function(val)
if cache == FROZEN then
return fn(val)
end
local cached = cache[val] local cached = cache[val]
if cached ~= FROZEN and cached ~= nil then if cached ~= FROZEN and cached ~= nil then
return unpack(cached) return unpack(cached)
@ -40,18 +43,30 @@ makecached = function(fn)
end end
return unpack(ret) return unpack(ret)
end end
local enable
enable = function()
if cache == FROZEN then
cache = { }
end
end
local disable
disable = function()
cache = FROZEN
end
return setmetatable({ return setmetatable({
get = get, get = get,
invalidate = invalidate, invalidate = invalidate,
freeze = freeze, freeze = freeze,
clear = clear clear = clear,
enable = enable,
disable = disable
}, { }, {
__call = function(self, val) __call = function(self, val)
return get(val) return get(val)
end end
}) })
end end
return { local cached = {
attributes = makecached(attributes), attributes = makecached(attributes),
dir = makecached(function(file) dir = makecached(function(file)
local _accum_0 = { } local _accum_0 = { }
@ -63,6 +78,31 @@ return {
return _accum_0 return _accum_0
end) end)
} }
local enable
enable = function()
for _, fn in cached do
fn:enable()
end
end
local disable
disable = function()
for _, fn in cached do
fn:disable()
end
end
local clear
clear = function()
for _, fn in cached do
fn:clear()
end
end
return setmetatable({
enable = enable,
disable = disable,
clear = clear
}, {
__index = cached
})
end end
end end
@ -70,10 +110,10 @@ end
do do
local _ENV = _ENV local _ENV = _ENV
package.preload[ "moonbuild.fsutil" ] = function( ... ) local arg = _G.arg; package.preload[ "moonbuild.fsutil" ] = function( ... ) local arg = _G.arg;
local dir, attributes local dir, attributes, clear, enable, disable
do do
local _obj_0 = require('moonbuild.fscache') local _obj_0 = require('moonbuild.fscache')
dir, attributes = _obj_0.dir, _obj_0.attributes dir, attributes, clear, enable, disable = _obj_0.dir, _obj_0.attributes, _obj_0.clear, _obj_0.enable, _obj_0.disable
end end
local gmatch, match, gsub, sub local gmatch, match, gsub, sub
do do
@ -98,22 +138,23 @@ normalizepath = function(file)
parts = _accum_0 parts = _accum_0
end end
local absolute = (sub(file, 1, 1)) == '/' local absolute = (sub(file, 1, 1)) == '/'
for i = 1, #parts do local i = 1
while i <= #parts do
local _continue_0 = false local _continue_0 = false
repeat repeat
if parts[i] == '.' then if parts[i] == '.' then
remove(parts, i) remove(parts, i)
i = i - 1
_continue_0 = true _continue_0 = true
break break
end end
if parts[i] == '..' and i ~= 1 then if parts[i] == '..' and i ~= 1 and parts[i - 1] ~= '..' then
remove(parts, i) remove(parts, i)
remove(parts, i - 1) remove(parts, i - 1)
i = i - 2 i = i - 1
_continue_0 = true _continue_0 = true
break break
end end
i = i + 1
_continue_0 = true _continue_0 = true
until true until true
if not _continue_0 then if not _continue_0 then
@ -121,7 +162,7 @@ normalizepath = function(file)
end end
end end
if #parts == 0 then if #parts == 0 then
return '.' return absolute and '/' or '.'
else else
return (absolute and '/' or '') .. concat(parts, '/') return (absolute and '/' or '') .. concat(parts, '/')
end end
@ -173,7 +214,12 @@ mtime = function(f)
end end
local matchglob local matchglob
matchglob = function(str, glob) matchglob = function(str, glob)
local patt = '^' .. (gsub((gsub(glob, '%*%*', '.*')), '%*', '[^/]*')) .. '$' glob = gsub(glob, '[%[%]%%+.?-]', function(self)
return '%' .. self
end)
local patt = '^' .. (gsub(glob, '%*%*?', function(self)
return self == '**' and '.*' or '[^/]*'
end)) .. '$'
local rst local rst
if (type(str)) == 'table' then if (type(str)) == 'table' then
local results, i = { }, 1 local results, i = { }, 1
@ -280,11 +326,6 @@ invalidatecache = function(file)
dir.invalidate(parentdir(file)) dir.invalidate(parentdir(file))
return attributes.invalidate(file) return attributes.invalidate(file)
end end
local clearcache
clearcache = function()
dir.clear()
return attributes.clear()
end
return { return {
wildcard = wildcard, wildcard = wildcard,
exists = exists, exists = exists,
@ -292,9 +333,12 @@ return {
mtime = mtime, mtime = mtime,
normalizepath = normalizepath, normalizepath = normalizepath,
parentdir = parentdir, parentdir = parentdir,
matchglob = matchglob,
freezecache = freezecache, freezecache = freezecache,
invalidatecache = invalidatecache, invalidatecache = invalidatecache,
clearcache = clearcache clearcache = clear,
enablecache = enable,
disablecache = disable
} }
end end

@ -16,6 +16,8 @@ makecached = (fn) ->
cache = {} cache = {}
get = (val) -> get = (val) ->
if cache == FROZEN
return fn val
cached = cache[val] cached = cache[val]
if cached!=FROZEN and cached!=nil if cached!=FROZEN and cached!=nil
return unpack cached return unpack cached
@ -24,10 +26,27 @@ makecached = (fn) ->
cache[val] = ret cache[val] = ret
unpack ret unpack ret
setmetatable { :get, :invalidate, :freeze, :clear }, enable = ->
cache = {} if cache==FROZEN
disable = ->
cache = FROZEN
setmetatable { :get, :invalidate, :freeze, :clear, :enable, :disable },
__call: (val) => get val __call: (val) => get val
{ cached = {
attributes: makecached attributes attributes: makecached attributes
dir: makecached (file) -> [k for k in dir file] dir: makecached (file) -> [k for k in dir file]
} }
enable = ->
fn\enable! for _, fn in cached
disable = ->
fn\disable! for _, fn in cached
clear = ->
fn\clear! for _, fn in cached
setmetatable { :enable, :disable, :clear }, __index: cached

@ -1,4 +1,4 @@
import dir, attributes from require 'moonbuild.fscache' import dir, attributes, clear, enable, disable from require 'moonbuild.fscache'
import gmatch, match, gsub, sub from string import gmatch, match, gsub, sub from string
import insert, remove, concat from table import insert, remove, concat from table
@ -6,18 +6,19 @@ import insert, remove, concat from table
normalizepath = (file) -> normalizepath = (file) ->
parts = [part for part in gmatch file, '[^/]+'] parts = [part for part in gmatch file, '[^/]+']
absolute = (sub file, 1, 1)=='/' absolute = (sub file, 1, 1)=='/'
for i=1, #parts i = 1
while i<=#parts
if parts[i]=='.' if parts[i]=='.'
remove parts, i remove parts, i
i -= 1
continue continue
if parts[i]=='..' and i!=1 if parts[i]=='..' and i!=1 and parts[i-1]!='..'
remove parts, i remove parts, i
remove parts, i-1 remove parts, i-1
i -= 2 i -= 1
continue continue
i += 1
if #parts==0 if #parts==0
'.' absolute and '/' or '.'
else else
(absolute and '/' or '') .. concat parts, '/' (absolute and '/' or '') .. concat parts, '/'
@ -40,7 +41,8 @@ mtime = (f) ->
a and a.modification a and a.modification
matchglob = (str, glob) -> matchglob = (str, glob) ->
patt = '^'..(gsub (gsub glob, '%*%*', '.*'), '%*', '[^/]*')..'$' glob = gsub glob, '[%[%]%%+.?-]', => '%'..@
patt = '^'..(gsub glob, '%*%*?', => @=='**' and '.*' or '[^/]*')..'$'
rst = if (type str)=='table' rst = if (type str)=='table'
results, i = {}, 1 results, i = {}, 1
for s in *str for s in *str
@ -111,14 +113,12 @@ invalidatecache = (file) ->
dir.invalidate parentdir file dir.invalidate parentdir file
attributes.invalidate file attributes.invalidate file
clearcache = ->
dir.clear!
attributes.clear!
{ {
:wildcard :wildcard
:exists, :isdir :exists, :isdir
:mtime :mtime
:normalizepath, :parentdir :normalizepath, :parentdir
:freezecache, :invalidatecache, :clearcache :matchglob
:freezecache, :invalidatecache
clearcache: clear, enablecache: enable, disablecache: disable
} }

@ -5,13 +5,25 @@ GLOB_PATT='^([^%%]*)%%([^%%]*)$'
patsubst = (str, pattern, replacement) -> patsubst = (str, pattern, replacement) ->
return [patsubst s, pattern, replacement for s in *str] if (type str)=='table' return [patsubst s, pattern, replacement for s in *str] if (type str)=='table'
if str==pattern
return replacement
prefix, suffix = match pattern, GLOB_PATT prefix, suffix = match pattern, GLOB_PATT
return str unless prefix if not (prefix or suffix)
return str
reprefix, resuffix = match replacement, GLOB_PATT reprefix, resuffix = match replacement, GLOB_PATT
return replacement unless reprefix if not (reprefix or resuffix)
if (#prefix==0 or (sub str, 1, #prefix)==prefix) and (#suffix==0 or (sub str, -#suffix)==suffix)
return replacement
else
return str
if (sub str, 1, #prefix)==prefix and (sub str, -#suffix)==suffix if #prefix==0 or (sub str, 1, #prefix)==prefix
return reprefix..(sub str, #prefix+1, -#suffix-1)..resuffix str = reprefix..(sub str, #prefix+1)
if #suffix==0 or (sub str, -#suffix)==suffix
str = (sub str, 1, -#suffix-1)..resuffix
str str
splitsp = (str) -> splitsp = (str) ->

@ -48,6 +48,9 @@ flatten = (tab) ->
out = {} out = {}
for e in *tab for e in *tab
if (type e)=='table' if (type e)=='table'
if e[1] == nil and (next e)!=nil
insert out, e
else
insert out, v for v in *flatten e insert out, v for v in *flatten e
else else
insert out, e insert out, e

@ -0,0 +1,111 @@
describe 'fsutil', ->
describe 'normalizepath', ->
import normalizepath from require 'moonbuild.fsutil'
test = (expected, source) ->
it "normalizes #{source} correctly", ->
assert.equal expected, normalizepath source
testall = (tab) ->
for a, b in pairs tab
test b, a
describe 'handles already normalized paths', ->
testall {
'.': '.'
'..': '..'
'../..': '../..'
'/': '/'
'/a': '/a'
'/a/b': '/a/b'
'a': 'a'
'a/b': 'a/b'
}
describe 'trims leading slashes', ->
testall {
'a/': 'a'
'a/b/': 'a/b'
'/a/': '/a'
'/a/b/': '/a/b'
}
describe 'normalizes absolute paths', ->
testall {
'/a/a/../b': '/a/b'
'/a/./b': '/a/b'
'/a/b/c/..': '/a/b'
'/./a/./b/././.': '/a/b'
}
describe 'normalizes relative paths', ->
testall {
'../x/../../a': '../../a'
'../x/../a': '../a'
'x/..': '.'
'../.': '..'
'./a': 'a'
}
describe 'matchglob', ->
import matchglob from require 'moonbuild.fsutil'
test = (expected, source, glob) ->
if expected
it "matches #{glob} on #{source}", ->
assert.equal source, matchglob source, glob
else
it "doesn't match #{glob} on #{source}", ->
assert.equal nil, matchglob source, glob
testall = (tab) ->
for a, b in pairs tab
test b, a[1], a[2]
describe 'handles literal names', ->
testall {
[{'a', 'a'}]: true
[{'a.b', 'a.b'}]: true
[{'a/b', 'a/b'}]: true
[{'..', '..'}]: true
}
describe 'doesn\'t treat things as special chars', ->
testall {
[{'a', '.'}]: false
[{'a.b.c', '%S+'}]: false
[{'%S+', '%S+'}]: true
[{'%d', '%d'}]: true
[{'a', '%S'}]: false
[{'aaa', 'a+'}]: false
}
describe 'only matches fully', ->
testall {
[{'abcdef', 'bcde'}]: false
[{'a/b/c', 'b/c'}]: false
[{'a/b/c', 'a/b'}]: false
}
describe 'handles *', ->
testall {
[{'abcde', '*'}]: true
[{'a/b/c/d', 'a/*/c/d'}]: true
[{'a/b/c/d', 'a/*/d'}]: false
[{'abcde', 'a*e'}]: true
[{'abcde', 'a*f'}]: false
[{'a/b/c/d/e', 'a/*/*/*/e'}]: true
[{'a/b/c/d/e', 'a*/*/*e'}]: false
}
describe 'handles **', ->
testall {
[{'abcde', '**'}]: true
[{'a/b/c/d', 'a/**/c/d'}]: true
[{'abcde', 'a**e'}]: true
[{'a/b/c/d/e', 'a/**/**/**/e'}]: true
[{'a/b/c/d/e', 'a**e'}]: true
[{'a/b/c/d/e', 'a/**/e'}]: true
[{'a/b/c/d/e', 'a**f'}]: false
[{'abcde', 'a**f'}]: false
}

@ -0,0 +1,78 @@
describe 'stringutil', ->
describe 'patsubst', ->
import patsubst from require 'moonbuild.stringutil'
test = (expected, source, patt, subst) ->
it "substitutes #{source} into #{expected} with #{patt} and #{subst}", ->
assert.equal expected, patsubst source, patt, subst
testall = (tab) ->
for a, b in pairs tab
test b, a[1], a[2], a[3]
describe 'handles just adding pre/suffix', ->
testall {
[{'a', '%', '_%'}]: '_a'
[{'tx', '%', 'a_%'}]: 'a_tx'
[{'a', '%', '%_'}]: 'a_'
[{'tx', '%', '%_a'}]: 'tx_a'
[{'a', '%', '_%_'}]: '_a_'
}
describe 'handles doing nothing', ->
for str in *({'a', 'aa', 'tx'})
test str, str, '%', '%'
describe 'handles literal change', ->
testall {
[{'a', 'a', 'b'}]: 'b'
[{'a', 'b', 'c'}]: 'a'
[{'aa', 'a', 'b'}]: 'aa'
}
describe 'handles match change', ->
testall {
[{'-a_', '-%_', 'b'}]: 'b'
[{'-a_', '-%', 'b'}]: 'b'
[{'-a_', '%_', 'b'}]: 'b'
[{'-a_', '_%-', 'b'}]: '-a_'
}
describe 'handles just removing pre/suffix', ->
testall {
[{'_a', '_%', '%'}]: 'a'
[{'a_', '%_', '%'}]: 'a'
[{'_a_', '_%_', '%'}]: 'a'
}
describe 'handles not matching', ->
testall {
[{'a-', '%_', '%'}]: 'a-'
[{'-a', '_%', '%'}]: '-a'
[{'-a-', '_%_', '%'}]: '-a-'
}
describe 'handles changing pre/suffix', ->
testall {
[{'a-', '%-', '%_'}]: 'a_'
[{'-a', '-%', '_%'}]: '_a'
[{'-a', '-%', '%_'}]: 'a_'
[{'_a-', '_%-', '-%_'}]: '-a_'
}
describe 'splitsp', ->
import splitsp from require 'moonbuild.stringutil'
test = (expected, source) ->
it "splits '#{source}' correctly", ->
assert.same expected, splitsp source
for source, expected in pairs {
'a b c': {'a', 'b', 'c'}
'abc': {'abc'}
'': {}
' a b c': {'a', 'b', 'c'}
' ': {}
' ab c': {'ab', 'c'}
}
test expected, source

@ -0,0 +1,82 @@
describe 'tableutil', ->
describe 'sortedpairs', ->
import sortedpairs from require 'moonbuild.tableutil'
for src, dst in pairs {
[{a: '1', c: 2, b: 3}]: {'a', '1', 'b', 3, 'c', 2}
[{5, 4, 3}]: {1, 5, 2, 4, 3, 3}
}
it "works for #{src}", ->
i = 1
for k, v in sortedpairs src
assert.equal k, dst[i]
assert.equal v, dst[i+1]
i += 2
describe 'min and max', ->
import min, max from require 'moonbuild.tableutil'
for src, dst in pairs {
[{1, 2, 3, 4, 5}]: {1, 5}
[{5, 4, 3, 2, 1}]: {1, 5}
[{2, 4, 5, 1, 3}]: {1, 5}
[{1, 1, 1, 1, 1}]: {1, 1}
[{1}]: {1, 1}
}
it "min of #{table.concat src, ','} is #{dst[1]}", ->
assert.equal dst[1], min src
it "max of #{table.concat src, ','} is #{dst[2]}", ->
assert.equal dst[2], max src
describe 'foreach', ->
import foreach from require 'moonbuild.tableutil'
src = {1, 2, 5, '79'}
testall = (name, rst, fn) ->
it name, ->
assert.same rst, foreach src, fn
testall 'works with tostring', {'1', '2', '5', '79'}, tostring
testall 'works with tonumber', {1, 2, 5, 79}, tonumber
testall 'works with some mix of tonumber and comparison', {false, false, true, true}, => 3<tonumber @
describe 'first', ->
import first from require 'moonbuild.tableutil'
test = (name, src, rst, fn) ->
it name, ->
assert.equal rst, (first src, fn)
test 'works with == for first of list', {1, 3, 5}, 1, => @==1
test 'works with == for something else', {1, 3, 5}, 3, => @==3
test 'works with == for absent element', {1, 3, 5}, nil, => @==2
describe 'exclude', ->
import exclude from require 'moonbuild.tableutil'
unpack or= table.unpack
test = (name, src, rst, ...) ->
rest = {...}
it name, ->
assert.equal src, exclude src, unpack rest
assert.same rst, src
test 'works with nothing', {1, 2, 3}, {1, 2, 3}
test 'works with absent elements', {1, 2, 3}, {1, 2, 3}, 4, 5, 6
test 'works with some elements', {1, 2, 3}, {1}, 2, 3
test 'works with all elements', {1, 2, 3}, {}, 1, 2, 3
test 'works with a mix', {1, 2, 3}, {1, 3}, 2, 4, 5
describe 'flatten', ->
import flatten from require 'moonbuild.tableutil'
test = (name, src, rst) ->
it name, ->
assert.same rst, flatten src
test 'works with empty table', {}, {}
test 'works with flat table', {1, 2, 3}, {1, 2, 3}
test 'works with one level', {1, {2}, {3}}, {1, 2, 3}
test 'works with multiple levels', {{{1, {{2}}}, 3}}, {1, 2, 3}
test 'skips maps', {1, {a: 2}, 3}, {1, {a: 2}, 3}
Loading…
Cancel
Save