summaryrefslogtreecommitdiff
path: root/Data/Libraries/Penlight/spec
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2021-10-30 11:32:16 +0800
committerchai <chaifix@163.com>2021-10-30 11:32:16 +0800
commit42ec7286b2d36a9ba22925f816a17cb1cc2aa5ce (patch)
tree24bc7009457a8d7500f264e89946dc20d069294f /Data/Libraries/Penlight/spec
parent164885fd98d48703bd771f802d79557b7db97431 (diff)
+ Penlight
Diffstat (limited to 'Data/Libraries/Penlight/spec')
-rw-r--r--Data/Libraries/Penlight/spec/app_spec.lua27
-rw-r--r--Data/Libraries/Penlight/spec/array2d_spec.lua557
-rw-r--r--Data/Libraries/Penlight/spec/date_spec.lua48
-rw-r--r--Data/Libraries/Penlight/spec/multimap_spec.lua14
-rw-r--r--Data/Libraries/Penlight/spec/permute_spec.lua213
-rw-r--r--Data/Libraries/Penlight/spec/pretty_spec.lua40
-rw-r--r--Data/Libraries/Penlight/spec/set_spec.lua84
-rw-r--r--Data/Libraries/Penlight/spec/text_spec.lua11
-rw-r--r--Data/Libraries/Penlight/spec/utils-deprecate_spec.lua128
-rw-r--r--Data/Libraries/Penlight/spec/utils-npairs_spec.lua105
10 files changed, 1227 insertions, 0 deletions
diff --git a/Data/Libraries/Penlight/spec/app_spec.lua b/Data/Libraries/Penlight/spec/app_spec.lua
new file mode 100644
index 0000000..3b1f290
--- /dev/null
+++ b/Data/Libraries/Penlight/spec/app_spec.lua
@@ -0,0 +1,27 @@
+local app = require("pl.app")
+
+describe("pl.app.lua", function ()
+
+ local invocation = app.lua()
+
+ it("should pick up the arguments used to run this test", function ()
+ assert.is.truthy(invocation:match("lua.+package.+busted"))
+ end)
+
+ it("should be reusable to invoke Lua", function ()
+ assert.is.truthy(os.execute(app.lua()..' -e "n=1;os.exit(n-1)"'))
+ end)
+
+end)
+
+describe("pl.app.platform", function ()
+
+ -- TODO: Find a reliable alternate way to determine platform to check that
+ -- this is returning the right answer, not just any old answer.
+ it("should at least return a valid platform", function ()
+ local platforms = { Linux = true, OSX = true, Windows = true }
+ local detected = app.platform()
+ assert.is.truthy(platforms[detected])
+ end)
+
+end)
diff --git a/Data/Libraries/Penlight/spec/array2d_spec.lua b/Data/Libraries/Penlight/spec/array2d_spec.lua
new file mode 100644
index 0000000..3fd0085
--- /dev/null
+++ b/Data/Libraries/Penlight/spec/array2d_spec.lua
@@ -0,0 +1,557 @@
+local array2d = require("pl.array2d")
+
+describe("pl.array2d", function()
+
+ describe("new()", function()
+ it("creates an empty 2d array", function()
+ assert.same({{},{},{}}, array2d.new(3,3,nil))
+ end)
+
+ it("creates a value-filled 2d array", function()
+ assert.same({{99,99,99},
+ {99,99,99},
+ {99,99,99}}, array2d.new(3,3,99))
+ end)
+
+ it("creates a function-filled 2d array", function()
+ assert.same({{2,3,4},
+ {3,4,5},
+ {4,5,6}}, array2d.new(3,3,function(i,j) return i+j end))
+ end)
+ end)
+
+ describe("size()", function()
+ it("returns array size", function()
+ local a = array2d.new(3,5,99)
+ assert.same({3,5}, {array2d.size(a)})
+ end)
+
+ it("returns 0 columns for nil arrays", function()
+ local a = array2d.new(3,5,nil)
+ assert.same({3,0}, {array2d.size(a)})
+ end)
+ end)
+
+ describe("column()", function()
+ it("returns a column copy", function()
+ local a = {{1,2},
+ {3,4},
+ {5,6}}
+ assert.same({1,3,5}, array2d.column(a,1))
+ assert.same({2,4,6}, array2d.column(a,2))
+ end)
+ end)
+
+ describe("row()", function()
+ it("returns a row copy", function()
+ local a = {{1,2},
+ {3,4},
+ {5,6}}
+ assert.same({1,2}, array2d.row(a,1))
+ -- next test: need to remove the metatable to prevent comparison by
+ -- metamethods in Lua 5.3 and 5.4
+ assert.not_equal(a[1], setmetatable(array2d.row(a,1),nil))
+ assert.same({3,4}, array2d.row(a,2))
+ assert.same({5,6}, array2d.row(a,3))
+ end)
+ end)
+
+ describe("map()", function()
+ it("maps a function on an array", function()
+ local a1 = array2d.new(2,3,function(i,j) return i+j end)
+ local a2 = array2d.map(function(a,b) return a .. b end, a1, "x")
+ assert.same({{"2x","3x","4x"},
+ {"3x","4x","5x"}}, a2)
+ end)
+ end)
+
+ describe("reduce_rows()", function()
+ it("reduces rows", function()
+ local a = {{ 1, 2, 3, 4},
+ { 10, 20, 30, 40},
+ { 100, 200, 300, 400},
+ {1000,2000,3000,4000}}
+ assert.same({10,100,1000,10000},array2d.reduce_rows('+',a))
+ end)
+ end)
+
+ describe("reduce_cols()", function()
+ it("reduces columns", function()
+ local a = {{ 1, 2, 3, 4},
+ { 10, 20, 30, 40},
+ { 100, 200, 300, 400},
+ {1000,2000,3000,4000}}
+ assert.same({1111,2222,3333,4444},array2d.reduce_cols('+',a))
+ end)
+ end)
+
+ describe("reduce2()", function()
+ it("recuces array to scalar", function()
+ local a = {{1,10},
+ {2,10},
+ {3,10}}
+ assert.same(60, array2d.reduce2('+','*',a))
+ end)
+ end)
+
+ describe("map2()", function()
+ it("maps over 2 arrays", function()
+ local b = {{10,20},
+ {30,40}}
+ local a = {{1,2},
+ {3,4}}
+ -- 2 2d arrays
+ assert.same({{11,22},{33,44}}, array2d.map2('+',2,2,a,b))
+ -- 1d, 2d
+ assert.same({{11,102},{13,104}}, array2d.map2('+',1,2,{10,100},a))
+ -- 2d, 1d
+ assert.same({{1,-2},{3,-4}},array2d.map2('*',2,1,a,{1,-1}))
+ end)
+ end)
+
+ describe("product()", function()
+ it("creates a product array", function()
+ local a = array2d.product('..',{1,2,3},{'a','b','c'})
+ assert.same({{'1a','2a','3a'},{'1b','2b','3b'},{'1c','2c','3c'}}, a)
+
+ local a = array2d.product('{}',{1,2},{'a','b','c'})
+ assert.same({{{1,'a'},{2,'a'}},{{1,'b'},{2,'b'}},{{1,'c'},{2,'c'}}}, a)
+ end)
+ end)
+
+ describe("flatten()", function()
+ it("flattens a 2darray", function()
+ local a = {{1,2},
+ {3,4},
+ {5,6}}
+ assert.same( {1,2,3,4,5,6}, array2d.flatten(a))
+ end)
+
+ it("keeps a nil-array 'square'", function()
+ local a = {{ 1,2},
+ {nil,4},
+ {nil,6}}
+ assert.same( {1,2,nil,4,nil,6}, array2d.flatten(a))
+ end)
+ end)
+
+ describe("reshape()", function()
+ it("reshapes array in new nr of rows", function()
+ local a = {{ 1, 2, 3},
+ { 4, 5, 6},
+ { 7, 8, 9},
+ {10,11,12}}
+ local b = array2d.reshape(a, 2, false)
+ assert.same({{ 1, 2, 3, 4, 5, 6},
+ { 7, 8, 9,10,11,12}}, b)
+ local c = array2d.reshape(b, 4, false)
+ assert.same(a, c)
+ end)
+ it("reshapes array in new nr of rows, column order", function()
+ local a = {{ 1, 2, 3},
+ { 4, 5, 6},
+ { 7, 8, 9}}
+ local b = array2d.reshape(a, 3, true)
+ assert.same({{ 1, 4, 7},
+ { 2, 5, 8},
+ { 3, 6, 9}}, b)
+ end)
+ end)
+
+ describe("transpose()", function()
+ it("transposes a 2d array", function()
+ local a = {{ 1, 2, 3},
+ { 4, 5, 6},
+ { 7, 8, 9}}
+ local b = array2d.transpose(a)
+ assert.same({{ 1, 4, 7},
+ { 2, 5, 8},
+ { 3, 6, 9}}, b)
+
+ local a = {{ 1, 2, 3, 4, 5},
+ { 6, 7, 8, 9, 10}}
+ local b = array2d.transpose(a)
+ assert.same({{ 1, 6},
+ { 2, 7},
+ { 3, 8},
+ { 4, 9},
+ { 5,10}}, b)
+ end)
+ end)
+
+ describe("swap_rows()", function()
+ it("swaps 2 rows, in-place", function()
+ local a = {{1,2},
+ {3,4},
+ {5,6}}
+ local b = array2d.swap_rows(a, 1, 3)
+ assert.same({{5,6},
+ {3,4},
+ {1,2}}, b)
+ assert.equal(a, b)
+ end)
+ end)
+
+ describe("swap_cols()", function()
+ it("swaps 2 columns, in-place", function()
+ local a = {{1,2,3},
+ {4,5,6},
+ {7,8,9}}
+ local b = array2d.swap_cols(a, 1, 3)
+ assert.same({{3,2,1},
+ {6,5,4},
+ {9,8,7}}, b)
+ assert.equal(a, b)
+ end)
+ end)
+
+ describe("extract_rows()", function()
+ it("extracts rows", function()
+ local a = {{ 1, 2, 3},
+ { 4, 5, 6},
+ { 7, 8, 9},
+ {10,11,12}}
+ local b = array2d.extract_rows(a, {1, 3})
+ assert.same({{1,2,3},
+ {7,8,9}}, b)
+ end)
+ end)
+
+ describe("extract_cols()", function()
+ it("extracts colums", function()
+ local a = {{ 1, 2, 3},
+ { 4, 5, 6},
+ { 7, 8, 9},
+ {10,11,12}}
+ local b = array2d.extract_cols(a, {1, 2})
+ assert.same({{ 1, 2},
+ { 4, 5},
+ { 7, 8},
+ {10,11}}, b)
+ end)
+ end)
+
+ describe("remove_row()", function()
+ it("removes a row", function()
+ local a = {{ 1, 2, 3},
+ { 4, 5, 6},
+ { 7, 8, 9},
+ {10,11,12}}
+ array2d.remove_row(a, 2)
+ assert.same({{ 1, 2, 3},
+ { 7, 8, 9},
+ {10,11,12}}, a)
+ end)
+ end)
+
+ describe("remove_col()", function()
+ it("removes a colum", function()
+ local a = {{ 1, 2, 3},
+ { 4, 5, 6},
+ { 7, 8, 9},
+ {10,11,12}}
+ array2d.remove_col(a, 2)
+ assert.same({{ 1, 3},
+ { 4, 6},
+ { 7, 9},
+ {10,12}}, a)
+ end)
+ end)
+
+ describe("parse_range()", function()
+ it("parses A1:B2 format", function()
+ assert.same({4,11,7,12},{array2d.parse_range("K4:L7")})
+ assert.same({4,28,7,54},{array2d.parse_range("AB4:BB7")})
+ -- test Col R since it might be mixed up with RxCx format
+ assert.same({4,18,7,18},{array2d.parse_range("R4:R7")})
+ end)
+
+ it("parses A1 format", function()
+ assert.same({4,11},{array2d.parse_range("K4")})
+ -- test Col R since it might be mixed up with RxCx format
+ assert.same({4,18},{array2d.parse_range("R4")})
+ end)
+
+ it("parses R1C1:R2C2 format", function()
+ assert.same({4,11,7,12},{array2d.parse_range("R4C11:R7C12")})
+ end)
+
+ it("parses R1C1 format", function()
+ assert.same({4,11},{array2d.parse_range("R4C11")})
+ end)
+ end)
+
+ describe("range()", function()
+ it("returns a range", function()
+ local a = {{1 ,2 ,3},
+ {4 ,5 ,6},
+ {7 ,8 ,9},
+ {10,11,12}}
+ local b = array2d.range(a, "B3:C4")
+ assert.same({{ 8, 9},
+ {11,12}}, b)
+ end)
+ end)
+
+ describe("default_range()", function()
+ it("returns the default range", function()
+ local a = array2d.new(4,6,1)
+ assert.same({1,1,4,6}, {array2d.default_range(a, nil, nil, nil, nil)})
+ end)
+
+ it("accepts negative indices", function()
+ local a = array2d.new(4,6,1)
+ assert.same({2,2,3,5}, {array2d.default_range(a, -3, -5, -2, -2)})
+ end)
+
+ it("corrects out of bounds indices", function()
+ local a = array2d.new(4,6,1)
+ assert.same({1,1,4,6}, {array2d.default_range(a, -100, -100, 100, 100)})
+ end)
+ end)
+
+ describe("slice()", function()
+ it("returns a slice", function()
+ local a = {{1 ,2 ,3},
+ {4 ,5 ,6},
+ {7 ,8 ,9},
+ {10,11,12}}
+ local b = array2d.slice(a,3,2,4,3)
+ assert.same({{ 8, 9},
+ {11,12}}, b)
+ end)
+
+ it("returns a single row if rows are equal", function()
+ local a = {{1 ,2 ,3},
+ {4 ,5 ,6},
+ {7 ,8 ,9},
+ {10,11,12}}
+ local b = array2d.slice(a,4,1,4,3)
+ assert.same({10,11,12}, b)
+ end)
+
+ it("returns a single column if columns are equal", function()
+ local a = {{1 ,2 ,3},
+ {4 ,5 ,6},
+ {7 ,8 ,9},
+ {10,11,12}}
+ local b = array2d.slice(a,1,3,4,3)
+ assert.same({3,6,9,12}, b)
+ end)
+
+ it("returns a single value if rows and columns are equal", function()
+ local a = {{1 ,2 ,3},
+ {4 ,5 ,6},
+ {7 ,8 ,9},
+ {10,11,12}}
+ local b = array2d.slice(a,2,2,2,2)
+ assert.same(5, b)
+ end)
+ end)
+
+ describe("set()", function()
+ it("sets a range to a value", function()
+ local a = {{1 ,2 ,3},
+ {4 ,5 ,6},
+ {7 ,8 ,9},
+ {10,11,12}}
+ array2d.set(a,0,2,2,3,3)
+ assert.same({{1 ,2 ,3},
+ {4 ,0 ,0},
+ {7 ,0 ,0},
+ {10,11,12}}, a)
+ end)
+
+ it("sets a range to a function value", function()
+ local a = {{1 ,2 ,3},
+ {4 ,5 ,6},
+ {7 ,8 ,9},
+ {10,11,12}}
+ local x = 10
+ local args = {}
+ local f = function(r,c)
+ args[#args+1] = {r,c}
+ x = x + 1
+ return x
+ end
+ array2d.set(a,f,3,1,4,3)
+ assert.same({{1 ,2 ,3},
+ {4 ,5 ,6},
+ {11,12,13},
+ {14,15,16}}, a)
+ -- validate args used to call the function
+ assert.same({{3,1},{3,2},{3,3},{4,1},{4,2},{4,3}}, args)
+ end)
+ end)
+
+ describe("write()", function()
+ it("writes array to a file", function()
+ local a = {{1 ,2 ,3},
+ {4 ,5 ,6},
+ {7 ,8 ,9},
+ {10,11,12}}
+ local f = setmetatable({}, {
+ __index = {
+ write = function(self,str)
+ self[#self+1] = str
+ end
+ }
+ })
+ array2d.write(a,f,"(%s)")
+ f = table.concat(f)
+ assert.equal([[(1)(2)(3)
+(4)(5)(6)
+(7)(8)(9)
+(10)(11)(12)
+]],f)
+ end)
+
+ it("writes partial array to a file", function()
+ local a = {{1 ,2 ,3},
+ {4 ,5 ,6},
+ {7 ,8 ,9},
+ {10,11,12}}
+ local f = setmetatable({}, {
+ __index = {
+ write = function(self,str)
+ self[#self+1] = str
+ end
+ }
+ })
+ array2d.write(a,f,"(%s)", 1,1,2,2)
+ f = table.concat(f)
+ assert.equal([[(1)(2)
+(4)(5)
+]],f)
+ end)
+ end)
+
+ describe("forall()", function()
+ it("runs all value and row functions", function()
+ local r = {}
+ local t = 0
+ local fval = function(row, j) t = t + row[j] end
+ local frow = function(i) r[#r+1] = t; t = 0 end
+ local a = {{1 ,2 ,3},
+ {4 ,5 ,6},
+ {7 ,8 ,9},
+ {10,11,12}}
+ array2d.forall(a, fval, frow)
+ assert.same({6, 15, 24, 33}, r)
+ r = {}
+ array2d.forall(a, fval, frow, 2,2,4,3)
+ assert.same({11, 17, 23}, r)
+ end)
+
+ end)
+
+ describe("move()", function()
+ it("moves block to destination aray", function()
+ local a = array2d.new(4,4,0)
+ local b = array2d.new(3,3,1)
+ array2d.move(a,2,2,b)
+ assert.same({{0,0,0,0},
+ {0,1,1,1},
+ {0,1,1,1},
+ {0,1,1,1}}, a)
+ end)
+ end)
+
+ describe("iter()", function()
+ it("iterates all values", function()
+ local a = {{1 ,2 ,3},
+ {4 ,5 ,6},
+ {7 ,8 ,9},
+ {10,11,12}}
+ local r = {}
+ for v, i, j in array2d.iter(a) do
+ r[#r+1] = v
+ assert.is_nil(i)
+ assert.is_nil(j)
+ end
+ assert.same({1,2,3,4,5,6,7,8,9,10,11,12}, r)
+ end)
+
+ it("iterates all values and indices", function()
+ local a = {{1 ,2 ,3},
+ {4 ,5 ,6},
+ {7 ,8 ,9},
+ {10,11,12}}
+ local r = {}
+ local ri = {}
+ local rj = {}
+ for i, j, v in array2d.iter(a,true) do
+ r[#r+1] = v
+ ri[#ri+1] = i
+ rj[#rj+1] = j
+ end
+ assert.same({1,2,3,4,5,6,7,8,9,10,11,12}, r)
+ assert.same({1,1,1,2,2,2,3,3,3,4,4,4}, ri)
+ assert.same({1,2,3,1,2,3,1,2,3,1,2,3}, rj)
+ end)
+
+ it("iterates all values of a 2d array part", function()
+ local a = {{1 ,2 ,3},
+ {4 ,5 ,6},
+ {7 ,8 ,9},
+ {10,11,12}}
+ local r = {}
+ for v, i, j in array2d.iter(a,false,2,2,4,3) do
+ r[#r+1] = v
+ assert.is_nil(i)
+ assert.is_nil(j)
+ end
+ assert.same({5,6,8,9,11,12}, r)
+ end)
+
+ it("iterates all values and indices of a 2d array part", function()
+ local a = {{1 ,2 ,3},
+ {4 ,5 ,6},
+ {7 ,8 ,9},
+ {10,11,12}}
+ local r = {}
+ local ri = {}
+ local rj = {}
+ for i, j, v in array2d.iter(a,true,2,2,4,3) do
+ r[#r+1] = v
+ ri[#ri+1] = i
+ rj[#rj+1] = j
+ end
+ assert.same({5,6,8,9,11,12}, r)
+ assert.same({2,2,3,3,4,4}, ri)
+ assert.same({2,3,2,3,2,3}, rj)
+ end)
+ end)
+
+ describe("columns()", function()
+ it("iterates all columns", function()
+ local a = {{1 ,2 ,3},
+ {4 ,5 ,6},
+ {7 ,8 ,9},
+ {10,11,12}}
+ local r = {}
+ for col, idx in array2d.columns(a) do
+ r[#r+1] = col
+ col.idx = idx
+ end
+ assert.same({{1,4,7,10, idx=1},{2,5,8,11, idx=2},{3,6,9,12, idx=3}}, r)
+ end)
+ end)
+
+ describe("rows()", function()
+ it("iterates all columns", function()
+ local a = {{1 ,2 ,3},
+ {4 ,5 ,6},
+ {7 ,8 ,9},
+ {10,11,12}}
+ local r = {}
+ for row, idx in array2d.rows(a) do
+ r[#r+1] = row
+ row.idx = idx
+ end
+ assert.same({{1,2,3, idx=1},{4,5,6, idx=2},
+ {7,8,9, idx=3},{10,11,12, idx=4}}, r)
+ end)
+ end)
+
+end)
diff --git a/Data/Libraries/Penlight/spec/date_spec.lua b/Data/Libraries/Penlight/spec/date_spec.lua
new file mode 100644
index 0000000..1032de2
--- /dev/null
+++ b/Data/Libraries/Penlight/spec/date_spec.lua
@@ -0,0 +1,48 @@
+local Date = require("pl.Date")
+
+describe("pl.Date", function ()
+
+ describe("function", function ()
+
+ describe("Format()", function ()
+
+ it("should output parsable inputs", function ()
+ local function assert_date_format(expected, format)
+ local df = Date.Format(format)
+ local d = df:parse(expected)
+ assert.is.equal(expected, df:tostring(d))
+ end
+ assert_date_format('02/04/10', 'dd/mm/yy')
+ assert_date_format('04/02/2010', 'mm/dd/yyyy')
+ assert_date_format('2011-02-20', 'yyyy-mm-dd')
+ assert_date_format('20070320', 'yyyymmdd')
+ assert_date_format('23:10', 'HH:MM')
+ end)
+
+ it("should parse 'slack' fields", function ()
+ local df = Date.Format("m/d/yy")
+ -- TODO: Re-enable when issue #359 fixed
+ -- assert.is.equal('01/05/99', df:tostring(df:parse('1/5/99')))
+ assert.is.equal('01/05/01', df:tostring(df:parse('1/5/01')))
+ assert.is.equal('01/05/32', df:tostring(df:parse('1/5/32')))
+ end)
+
+ end)
+
+ end)
+
+ describe("meta method", function ()
+
+ describe("__tostring()", function ()
+
+ it("should be suitable for serialization", function ()
+ local df = Date.Format()
+ local du = df:parse("2008-07-05")
+ assert.is.equal(du, du:toUTC())
+ end)
+
+ end)
+
+ end)
+
+end)
diff --git a/Data/Libraries/Penlight/spec/multimap_spec.lua b/Data/Libraries/Penlight/spec/multimap_spec.lua
new file mode 100644
index 0000000..a83d150
--- /dev/null
+++ b/Data/Libraries/Penlight/spec/multimap_spec.lua
@@ -0,0 +1,14 @@
+local MultiMap = require("pl.MultiMap")
+
+describe("pl.MultiMap", function ()
+
+ it("should hold multiple values per key", function ()
+ local map = MultiMap()
+ map:set('foo', 1)
+ map:set('bar', 3)
+ map:set('foo', 2)
+ local expected = { foo = { 1, 2 }, bar = { 3 } }
+ assert.is.same(expected, map)
+ end)
+
+end)
diff --git a/Data/Libraries/Penlight/spec/permute_spec.lua b/Data/Libraries/Penlight/spec/permute_spec.lua
new file mode 100644
index 0000000..49143c0
--- /dev/null
+++ b/Data/Libraries/Penlight/spec/permute_spec.lua
@@ -0,0 +1,213 @@
+local permute = require("pl.permute")
+local tcopy = require("pl.tablex").copy
+local utils = require("pl.utils")
+
+describe("pl.permute", function()
+
+ describe("order_iter", function()
+
+ it("returns all order combinations", function()
+ local result = {}
+ for list in permute.order_iter({"one", "two", "three"}) do
+ result[#result+1] = tcopy(list)
+ end
+ assert.same({
+ [1] = {
+ [1] = 'two',
+ [2] = 'three',
+ [3] = 'one' },
+ [2] = {
+ [1] = 'three',
+ [2] = 'two',
+ [3] = 'one' },
+ [3] = {
+ [1] = 'three',
+ [2] = 'one',
+ [3] = 'two' },
+ [4] = {
+ [1] = 'one',
+ [2] = 'three',
+ [3] = 'two' },
+ [5] = {
+ [1] = 'two',
+ [2] = 'one',
+ [3] = 'three' },
+ [6] = {
+ [1] = 'one',
+ [2] = 'two',
+ [3] = 'three' } }, result)
+ end)
+
+
+ it("returns nil on empty list", function()
+ local result = {}
+ for list in permute.order_iter({}) do
+ result[#result+1] = tcopy(list)
+ end
+ assert.equal(0, #result)
+ end)
+
+ end)
+
+
+
+ describe("order_table", function()
+
+ it("returns all order combinations", function()
+ local result = permute.order_table({"one", "two", "three"})
+ assert.same({
+ [1] = {
+ [1] = 'two',
+ [2] = 'three',
+ [3] = 'one' },
+ [2] = {
+ [1] = 'three',
+ [2] = 'two',
+ [3] = 'one' },
+ [3] = {
+ [1] = 'three',
+ [2] = 'one',
+ [3] = 'two' },
+ [4] = {
+ [1] = 'one',
+ [2] = 'three',
+ [3] = 'two' },
+ [5] = {
+ [1] = 'two',
+ [2] = 'one',
+ [3] = 'three' },
+ [6] = {
+ [1] = 'one',
+ [2] = 'two',
+ [3] = 'three' } }, result)
+ end)
+
+
+ it("returns empty table on empty input list", function()
+ local result = permute.order_table({})
+ assert.same({}, result)
+ end)
+
+ end)
+
+
+
+ describe("list_iter", function()
+
+ it("returns all combinations from sub-lists", function()
+ local result = {}
+ local strs = {"one", "two", "three"}
+ local ints = { 1,2,3 }
+ local bools = { true, false }
+ for count, str, int, bool in permute.list_iter(strs, ints, bools) do
+ result[#result+1] = {count, str, int, bool}
+ end
+ assert.same({
+ [1] = {1, 'one', 1, true },
+ [2] = {2, 'two', 1, true },
+ [3] = {3, 'three', 1, true },
+ [4] = {4, 'one', 2, true },
+ [5] = {5, 'two', 2, true },
+ [6] = {6, 'three', 2, true },
+ [7] = {7, 'one', 3, true },
+ [8] = {8, 'two', 3, true },
+ [9] = {9, 'three', 3, true },
+ [10] = {10, 'one', 1, false },
+ [11] = {11, 'two', 1, false },
+ [12] = {12, 'three', 1, false },
+ [13] = {13, 'one', 2, false },
+ [14] = {14, 'two', 2, false },
+ [15] = {15, 'three', 2, false },
+ [16] = {16, 'one', 3, false },
+ [17] = {17, 'two', 3, false },
+ [18] = {18, 'three', 3, false },
+ }, result)
+ end)
+
+
+ it("is nil-safe, given 'n' is set", function()
+ local result = {}
+ local bools = utils.pack(nil, true, false)
+ local strs = utils.pack("one", "two", nil)
+ for count, bool, str in permute.list_iter(bools, strs) do
+ result[#result+1] = {count, bool, str}
+ end
+ assert.same({
+ [1] = {1, nil, 'one' },
+ [2] = {2, true, 'one' },
+ [3] = {3, false, 'one' },
+ [4] = {4, nil, 'two' },
+ [5] = {5, true, 'two' },
+ [6] = {6, false, 'two' },
+ [7] = {7, nil, nil },
+ [8] = {8, true, nil },
+ [9] = {9, false, nil },
+ }, result)
+ end)
+
+
+ it("returns nil on empty list", function()
+ local count = 0
+ for list in permute.list_iter({}) do
+ count = count + 1
+ end
+ assert.equal(0, count)
+ end)
+
+ end)
+
+
+
+ describe("list_table", function()
+
+ it("returns all combinations from sub-lists", function()
+ local strs = {"one", "two", "three"}
+ local ints = { 1,2,3 }
+ local bools = { true, false }
+ assert.same({
+ [1] = {'one', 1, true, n = 3 },
+ [2] = {'two', 1, true, n = 3 },
+ [3] = {'three', 1, true, n = 3 },
+ [4] = {'one', 2, true, n = 3 },
+ [5] = {'two', 2, true, n = 3 },
+ [6] = {'three', 2, true, n = 3 },
+ [7] = {'one', 3, true, n = 3 },
+ [8] = {'two', 3, true, n = 3 },
+ [9] = {'three', 3, true, n = 3 },
+ [10] = {'one', 1, false, n = 3 },
+ [11] = {'two', 1, false, n = 3 },
+ [12] = {'three', 1, false, n = 3 },
+ [13] = {'one', 2, false, n = 3 },
+ [14] = {'two', 2, false, n = 3 },
+ [15] = {'three', 2, false, n = 3 },
+ [16] = {'one', 3, false, n = 3 },
+ [17] = {'two', 3, false, n = 3 },
+ [18] = {'three', 3, false, n = 3 },
+ }, permute.list_table(strs, ints, bools))
+ end)
+
+
+ it("is nil-safe, given 'n' is set", function()
+ local bools = utils.pack(nil, true, false)
+ local strs = utils.pack("one", "two", nil)
+ assert.same({
+ [1] = {nil, 'one', n = 2 },
+ [2] = {true, 'one', n = 2 },
+ [3] = {false, 'one', n = 2 },
+ [4] = {nil, 'two', n = 2 },
+ [5] = {true, 'two', n = 2 },
+ [6] = {false, 'two', n = 2 },
+ [7] = {nil, nil, n = 2 },
+ [8] = {true, nil, n = 2 },
+ [9] = {false, nil, n = 2 },
+ }, permute.list_table(bools, strs))
+ end)
+
+
+ it("returns nil on empty list", function()
+ assert.same({}, permute.list_table({}))
+ end)
+
+ end)
+
+end)
diff --git a/Data/Libraries/Penlight/spec/pretty_spec.lua b/Data/Libraries/Penlight/spec/pretty_spec.lua
new file mode 100644
index 0000000..85e3770
--- /dev/null
+++ b/Data/Libraries/Penlight/spec/pretty_spec.lua
@@ -0,0 +1,40 @@
+local pretty = require("pl.pretty")
+
+describe("pl.pretty.number", function ()
+
+ it("should format memory", function ()
+ local function assert_memory (expected, input)
+ assert.is.equal(expected, pretty.number(input, "M"))
+ end
+ assert_memory("123B", 123)
+ assert_memory("1.2KiB", 1234)
+ assert_memory("10.0KiB", 10*1024)
+ assert_memory("1.0MiB", 1024*1024)
+ assert_memory("1.0GiB", 1024*1024*1024)
+ end)
+
+ it("should format postfixes", function ()
+ local function assert_postfix(expected, input)
+ assert.is.equal(expected, pretty.number(input, "N", 2))
+ end
+ assert_postfix("123", 123)
+ assert_postfix("1.23K", 1234)
+ assert_postfix("10.24K", 10*1024)
+ assert_postfix("1.05M", 1024*1024)
+ assert_postfix("1.07B", 1024*1024*1024)
+ end)
+
+ it("should format postfixes", function ()
+ local function assert_separator(expected, input)
+ assert.is.equal(expected, pretty.number(input, "T"))
+ end
+ assert_separator('123', 123)
+ assert_separator('1,234', 1234)
+ assert_separator('12,345', 12345)
+ assert_separator('123,456', 123456)
+ assert_separator('1,234,567', 1234567)
+ assert_separator('12,345,678', 12345678)
+ end)
+
+
+end)
diff --git a/Data/Libraries/Penlight/spec/set_spec.lua b/Data/Libraries/Penlight/spec/set_spec.lua
new file mode 100644
index 0000000..02febca
--- /dev/null
+++ b/Data/Libraries/Penlight/spec/set_spec.lua
@@ -0,0 +1,84 @@
+local Set = require("pl.Set")
+
+describe("pl.Set", function ()
+
+ local s = Set()
+ local s1_2 = Set({ 1, 2 })
+ local s1_2_3 = Set({ 1, 2, 3 })
+ local s1_3 = Set({ 1, 3 })
+ local s2 = Set({ 2 })
+ local s2_1 = Set({ 2, 1 })
+ local s2_3 = Set({ 2, 3 })
+ local s3 = Set({ 3 })
+ local sm = Set({ "foo", "bar" })
+
+ it("should produce a set object", function ()
+ assert.is.same({ true, true }, s1_2)
+ end)
+
+ it("should produce identical sets for any ordered input", function ()
+ assert.is.same(s1_2, s2_1)
+ end)
+
+ describe("should have an operator for", function ()
+
+ it("union", function ()
+ assert.is.same(s1_2_3, s1_2 + s3)
+ assert.is.same(s1_2_3, s1_2 + 3)
+ end)
+
+ it("intersection", function ()
+ assert.is.same(s2, s1_2 * s2_3)
+ end)
+
+ it("difference", function ()
+ assert.is.same(s2_1, s1_2_3 - s3)
+ assert.is.same(s2_3, s1_2_3 - 1)
+ end)
+
+ it("symmetric difference", function ()
+ assert.is.same(s1_3, s1_2 ^ s2_3)
+ end)
+
+ it("tostring", function ()
+ -- Cannot test multi-entry sets because of non-deterministic key order
+ assert.is.same('[2]', tostring(s2))
+ end)
+
+ end)
+
+ describe("should provide functions", function ()
+
+ it("isempty", function ()
+ assert.is.truthy(Set.isempty(s))
+ assert.is.falsy(Set.isempty(s3))
+ end)
+
+ it("set", function ()
+ local m = Set()
+ Set.set(m, 'foo', true)
+ m.bar = true
+ assert.is.same(m, sm)
+ assert.is_not.same(m, s1_2)
+ end)
+
+ end)
+
+ describe("should have a comparison operator for", function ()
+
+ it("supersets/subsets than", function ()
+ assert.is.truthy(s1_2 > s2)
+ assert.is.falsy(s1_3 > s2)
+ assert.is.falsy(s1_2 > s2_3)
+ assert.is.truthy(s1_2 < s1_2_3)
+ assert.is.falsy(s1_2_3 < s1_2)
+ end)
+
+ it("equality", function ()
+ assert.is.truthy(s1_2 == s2_1)
+ assert.is.falsy(s1_2 == s2_3)
+ end)
+
+ end)
+
+end)
diff --git a/Data/Libraries/Penlight/spec/text_spec.lua b/Data/Libraries/Penlight/spec/text_spec.lua
new file mode 100644
index 0000000..cd42f06
--- /dev/null
+++ b/Data/Libraries/Penlight/spec/text_spec.lua
@@ -0,0 +1,11 @@
+local text = require("pl.text")
+
+describe("pl.text.Template", function ()
+
+ it("replaces placeholders", function ()
+ local tempalte = text.Template("${here} is the $answer")
+ local out = tempalte:substitute({ here = 'one', answer = 'two' })
+ assert.is.equal('one is the two', out)
+ end)
+
+end)
diff --git a/Data/Libraries/Penlight/spec/utils-deprecate_spec.lua b/Data/Libraries/Penlight/spec/utils-deprecate_spec.lua
new file mode 100644
index 0000000..1a6e352
--- /dev/null
+++ b/Data/Libraries/Penlight/spec/utils-deprecate_spec.lua
@@ -0,0 +1,128 @@
+local utils = require("pl.utils")
+
+describe("pl.utils", function ()
+
+ local old_fn, last_msg, last_trace
+
+ before_each(function()
+ old_fn = function() end
+ last_msg = nil
+ last_trace = nil
+ utils.set_deprecation_func(function(msg, trace)
+ last_msg = msg
+ last_trace = trace
+ end)
+ end)
+
+
+ after_each(function()
+ utils.deprecation_warning = old_fn
+ end)
+
+
+
+ describe("set_deprecation_func", function ()
+
+ it("accepts nil as callback", function()
+ assert.has.no.error(function()
+ utils.set_deprecation_func()
+ end)
+ end)
+
+
+ it("accepts function as callback", function()
+ assert.has.no.error(function()
+ utils.set_deprecation_func(function() end)
+ end)
+ end)
+
+
+ it("fails on non-functions", function()
+ assert.has.error(function()
+ utils.set_deprecation_func("not a function")
+ end, "argument 1 expected a 'function', got a 'string'")
+ end)
+
+ end)
+
+
+
+ describe("raise_deprecation", function ()
+
+ it("requires the opts table", function()
+ assert.has.error(function() utils.raise_deprecation(nil) end,
+ "argument 1 expected a 'table', got a 'nil'")
+ end)
+
+
+ it("requires the opts.message field", function()
+ assert.has.error(function() utils.raise_deprecation({}) end,
+ "field 'message' of the options table must be a string")
+ end)
+
+
+ it("should output the message", function ()
+ utils.raise_deprecation {
+ message = "hello world"
+ }
+ assert.equal("hello world", last_msg)
+ end)
+
+
+ it("should output the deprecated version", function ()
+ utils.raise_deprecation {
+ message = "hello world",
+ deprecated_after = "2.0.0",
+ }
+ assert.equal("hello world (deprecated after 2.0.0)", last_msg)
+ end)
+
+
+ it("should output the removal version", function ()
+ utils.raise_deprecation {
+ message = "hello world",
+ version_removed = "3.0.0",
+ }
+ assert.equal("hello world (scheduled for removal in 3.0.0)", last_msg)
+ end)
+
+
+ it("should output the deprecated and removal versions", function ()
+ utils.raise_deprecation {
+ message = "hello world",
+ deprecated_after = "2.0.0",
+ version_removed = "3.0.0",
+ }
+ assert.equal("hello world (deprecated after 2.0.0, scheduled for removal in 3.0.0)", last_msg)
+ end)
+
+
+ it("should output the application/module name", function ()
+ utils.raise_deprecation {
+ source = "MyApp 1.2.3",
+ message = "hello world",
+ deprecated_after = "2.0.0",
+ version_removed = "3.0.0",
+ }
+ assert.equal("[MyApp 1.2.3] hello world (deprecated after 2.0.0, scheduled for removal in 3.0.0)", last_msg)
+ end)
+
+
+ it("should add a stracktrace", function ()
+ local function my_function_name()
+ utils.raise_deprecation {
+ source = "MyApp 1.2.3",
+ message = "hello world",
+ deprecated_after = "2.0.0",
+ version_removed = "3.0.0",
+ }
+ end
+ my_function_name()
+
+ assert.Not.match("raise_deprecation", last_trace)
+ assert.match("my_function_name", last_trace)
+ end)
+
+ end)
+
+end)
diff --git a/Data/Libraries/Penlight/spec/utils-npairs_spec.lua b/Data/Libraries/Penlight/spec/utils-npairs_spec.lua
new file mode 100644
index 0000000..7e3c5d4
--- /dev/null
+++ b/Data/Libraries/Penlight/spec/utils-npairs_spec.lua
@@ -0,0 +1,105 @@
+local utils = require("pl.utils")
+
+describe("pl.utils", function ()
+
+ describe("npairs", function ()
+ local npairs = utils.npairs
+
+ it("start index defaults to 1", function()
+ local t1 = { 1, 2, 3 }
+ local t2 = {}
+ for i, v in npairs(t1, nil, 2) do t2[i] = v end
+ assert.are.same({ 1, 2 }, t2)
+ end)
+
+
+ it("end index defaults to `t.n`", function()
+ local t1 = { n = 2, 1, 2, 3 }
+ local t2 = {}
+ for i, v in npairs(t1) do t2[i] = v end
+ assert.are.same({1, 2}, t2)
+ end)
+
+
+ it("step size defaults to 1", function()
+ local t1 = { 1, 2, 3 }
+ local t2 = {}
+ for i, v in npairs(t1) do t2[i] = v end
+ assert.are.same({1, 2, 3}, t2)
+ end)
+
+
+ it("step size cannot be 0", function()
+ local t1 = { 1, 2, 3 }
+ assert.has.error(function()
+ npairs(t1, nil, nil, 0)
+ end, "iterator step-size cannot be 0")
+ end)
+
+
+ it("end index defaults to `#t` if there is no `t.n`", function()
+ local t1 = { 1, 2, 3 }
+ local t2 = {}
+ for i, v in npairs(t1) do t2[i] = v end
+ assert.are.same({1, 2, 3}, t2)
+ end)
+
+
+ it("returns nothing if start index is beyond end index", function()
+ local t1 = { 1, 2, 3 }
+ local t2 = {}
+ for i, v in npairs(t1, 5, 3) do t2[i] = v end
+ assert.are.same({}, t2)
+ end)
+
+
+ it("returns nothing if start index is beyond end index, with negative step size", function()
+ local t1 = { 1, 2, 3 }
+ local t2 = {}
+ for i, v in npairs(t1, 3, 1, -1) do t2[#t2+1] = v end
+ assert.are.same({ 3, 2, 1}, t2)
+ end)
+
+
+ it("returns 1 key/value if end == start index", function()
+ local t1 = { 1, 2, 3 }
+ local t2 = {}
+ for i, v in npairs(t1, 2, 2) do t2[i] = v end
+ assert.are.same({ [2] = 2 }, t2)
+ end)
+
+
+ it("returns negative to positive ranges", function()
+ local t1 = { [-5] = -5, [-4] = -4, [-3] = -3, [-2] = -2, [-1] = -1, [0] = 0, 1, 2, 3 }
+ local t2 = {}
+ for i, v in npairs(t1, -4, 1) do t2[i] = v end
+ assert.are.same({ [-4] = -4, [-3] = -3, [-2] = -2, [-1] = -1, [0] = 0, 1 }, t2)
+ end)
+
+
+ it("returns nil values with the range", function()
+ local t1 = { n = 3 }
+ local t2 = {}
+ for i, v in npairs(t1) do t2[i] = tostring(v) end
+ assert.are.same({ "nil", "nil", "nil" }, t2)
+ end)
+
+
+ it("honours positive step size", function()
+ local t1 = { [-5] = -5, [-4] = -4, [-3] = -3, [-2] = -2, [-1] = -1, [0] = 0, 1, 2, 3 }
+ local t2 = {}
+ for i, v in npairs(t1, -4, 1, 2) do t2[#t2+1] = v end
+ assert.are.same({ -4, -2, 0}, t2)
+ end)
+
+
+ it("honours negative step size", function()
+ local t1 = { [-5] = -5, [-4] = -4, [-3] = -3, [-2] = -2, [-1] = -1, [0] = 0, 1, 2, 3 }
+ local t2 = {}
+ for i, v in npairs(t1, 0, -5, -2) do t2[#t2+1] = v end
+ assert.are.same({ 0, -2, -4 }, t2)
+ end)
+
+ end)
+
+end)