-- Unit tests for [[Module:TableTools]]. Click talk page to run tests.

local tt = require('Module:TableTools/sandbox') -- the module to be tested
local ScribuntoUnit = require('Module:ScribuntoUnit')
local suite = ScribuntoUnit:new()

------------------------------------------------------------------------
-- Helper functions
------------------------------------------------------------------------

function suite.concatIpairs(t)
	local ret = ''
	for i, v in ipairs(t) do
		ret = ret .. tostring(v)
	end
	return ret
end

suite.isNan = tt.isNan

function suite:assertErrorEquals(expected, func, ...)
	local success, msg = pcall(func, ...)
	self:assertEquals(expected, msg)
end

function suite:assertTypeErrorEquals(argId, name, expectedType, actualType, func, ...)
	local expected = "bad argument #%d to '%s' (%s expected, got %s)"
	expected = expected:format(argId, name, expectedType, actualType)
	self:assertErrorEquals(expected, func, ...)
end

------------------------------------------------------------------------
-- Test isPositiveInteger
------------------------------------------------------------------------

function suite:testIsPositiveInteger()
	self:assertTrue(tt.isPositiveInteger(1))
	self:assertTrue(tt.isPositiveInteger(2))
	self:assertTrue(tt.isPositiveInteger(2323874623))
	self:assertFalse(tt.isPositiveInteger(0))
	self:assertFalse(tt.isPositiveInteger(-1))
	self:assertFalse(tt.isPositiveInteger(0.5))
	self:assertFalse(tt.isPositiveInteger(1.5))
	self:assertFalse(tt.isPositiveInteger('1'))
	self:assertFalse(tt.isPositiveInteger(math.huge))
	self:assertFalse(tt.isPositiveInteger('foo'))
end

------------------------------------------------------------------------
-- Test isNan
------------------------------------------------------------------------

function suite:testIsNan()
	self:assertTrue(tt.isNan(0/0))
	self:assertTrue(tt.isNan(-(0/0)))
	self:assertTrue(tt.isNan(math.huge * 0))
	self:assertTrue(tt.isNan(-math.huge * 0))
	self:assertTrue(tt.isNan(-1 * math.huge * 0))
	self:assertFalse(tt.isNan(0))
	self:assertFalse(tt.isNan(1))
	self:assertFalse(tt.isNan(6))
	self:assertFalse(tt.isNan(-100))
	self:assertFalse(tt.isNan(2^0.5))
	self:assertFalse(tt.isNan(99999999999999999999999999999999999999999999999))
	self:assertFalse(tt.isNan(math.pi))
end

------------------------------------------------------------------------
-- Test removeDuplicates
------------------------------------------------------------------------

function suite:testRemoveDuplicates()
	local dupes =     {1, 2, 2, 3, 3, 3, 4, 2, 0/0, 5, -(0/0), 5}
	-- Dupes removed: {1, 2,    3,       4,    NaN, 5, NaN}
	local removed = tt.removeDuplicates(dupes)
	self:assertEquals(7, #removed)
	self:assertEquals(1, removed[1])
	self:assertEquals(2, removed[2])
	self:assertEquals(3, removed[3])
	self:assertEquals(4, removed[4])
	self:assertTrue(suite.isNan(removed[5]))
	self:assertEquals(5, removed[6])
	self:assertTrue(suite.isNan(removed[7]))
	self:assertTypeErrorEquals(1, 'removeDuplicates', 'table', 'number', tt.removeDuplicates, 4)
	self:assertTypeErrorEquals(1, 'removeDuplicates', 'table', 'nil', tt.removeDuplicates)
end

------------------------------------------------------------------------
-- Sparse array variables
------------------------------------------------------------------------

local sparseArray = {1, nil, 2, nil, 3, nil, [math.huge] = math.huge, foo = 'bar', [1.5] = 1.5, ['4'] = 'four_string'}
local sparseArrayConcatenated = '123'
local numKeysConcatenated = '135'
local reverseSparseArrayConcatenated = '321'
local reverseNumKeysConcatenated = '531'

------------------------------------------------------------------------
-- Test numKeys
------------------------------------------------------------------------

function suite:testnumKeys()
	local numKeys = tt.numKeys(sparseArray)
    self:assertEquals(numKeysConcatenated, suite.concatIpairs(numKeys))
	self:assertTypeErrorEquals(1, 'numKeys', 'table', 'number', tt.numKeys, 4)
	self:assertTypeErrorEquals(1, 'numKeys', 'table', 'nil', tt.numKeys)
end

------------------------------------------------------------------------
-- Test affixNums
------------------------------------------------------------------------

local affixArray = {1, a0 = 'a0', a001 = 'a001', a1 = 'a1', b2 = 'b2', a3 = 'a3', c4d = 'c4d', b5 = 'b5', B6 = 'B6', f7 = 'f7', c8d = 'c8d', a9 = nil, a10 = 'a10', [11] = 11, ['e-f12'] = 'e-f12'}
local aNumsConcatenated = '1310'
local aValsConcatenated = 'a1a3a10'
local bNumsConcatenated = '25'
local bValsConcatenated = 'b2b5'
local cdNumsConcatenated = '48'
local cdValsConcatenated = 'c4dc8d'
local efNumsConcatenated = '12'
local efValsConcatenated = 'e-f12'

function suite.concatAffixVals(t, nums, prefix, suffix)
	local ret = ''
	for i, num in ipairs(nums) do
		local key = (prefix or '') .. tostring(num) .. (suffix or '')
		ret = ret .. tostring(t[key])
	end
	return ret
end

function suite:testaffixNums()
	local aNums = tt.affixNums(affixArray, 'a')
	local bNums = tt.affixNums(affixArray, 'b')
	local cdNums = tt.affixNums(affixArray, 'c', 'd')
	local efNums = tt.affixNums(affixArray, 'e-f') -- "-" is magic, so we need to escape it.
    self:assertEquals(aNumsConcatenated, suite.concatIpairs(aNums))
    self:assertEquals(aValsConcatenated, suite.concatAffixVals(affixArray, aNums, 'a'))
    self:assertEquals(bNumsConcatenated, suite.concatIpairs(bNums))
    self:assertEquals(bValsConcatenated, suite.concatAffixVals(affixArray, bNums, 'b'))
    self:assertEquals(cdNumsConcatenated, suite.concatIpairs(cdNums))
    self:assertEquals(cdValsConcatenated, suite.concatAffixVals(affixArray, cdNums, 'c', 'd'))
    self:assertEquals(efNumsConcatenated, suite.concatIpairs(efNums))
    self:assertEquals(efValsConcatenated, suite.concatAffixVals(affixArray, efNums, 'e-f'))
	self:assertTypeErrorEquals(1, 'affixNums', 'table', 'number', tt.affixNums, 4)
	self:assertTypeErrorEquals(1, 'affixNums', 'table', 'nil', tt.affixNums)
end

------------------------------------------------------------------------
-- Test numData
------------------------------------------------------------------------

function suite:testNumData()
	local t = {1, 2, [5] = 5, foo = "foo", foo1 = "foo1", bar1 = "bar1", foo6 = "foo6", bar6 = "bar6"}
	local uncompressed = tt.numData(t)
	local compressed = tt.numData(t, true)

	-- Test uncompressed.
	self:assertEquals(1, uncompressed[1][1])
	self:assertEquals(2, uncompressed[2][1])
	self:assertEquals(5, uncompressed[5][1])
	self:assertEquals("foo", uncompressed.other.foo)
	self:assertEquals("foo1", uncompressed[1].foo)
	self:assertEquals("bar1", uncompressed[1].bar)
	self:assertEquals("foo6", uncompressed[6].foo)
	self:assertEquals("bar6", uncompressed[6].bar)
	self:assertEquals(nil, uncompressed[4])

	-- Test compressed.
	self:assertEquals(1, compressed[1][1])
	self:assertEquals(2, compressed[2][1])
	self:assertEquals(5, compressed[3][1])
	self:assertEquals("foo", compressed.other.foo)
	self:assertEquals("foo1", compressed[1].foo)
	self:assertEquals("bar1", compressed[1].bar)
	self:assertEquals("foo6", compressed[4].foo)
	self:assertEquals("bar6", compressed[4].bar)
	self:assertEquals(nil, compressed[5])
end

------------------------------------------------------------------------
-- Test sparse array functions
------------------------------------------------------------------------

function suite:testCompressSparseArray()
	local compressedArray = tt.compressSparseArray(sparseArray)
    self:assertEquals(sparseArrayConcatenated, suite.concatIpairs(compressedArray))
	self:assertTypeErrorEquals(1, 'compressSparseArray', 'table', 'number', tt.compressSparseArray, 4)
	self:assertTypeErrorEquals(1, 'compressSparseArray', 'table', 'nil', tt.compressSparseArray)
end

function suite:testSparseIpairs()
	local arrayText = ''
	for i, v in tt.sparseIpairs(sparseArray) do
		arrayText = arrayText .. tostring(v)
	end
	self:assertEquals(sparseArrayConcatenated, arrayText)
	self:assertTypeErrorEquals(1, 'sparseIpairs', 'table', 'number', tt.sparseIpairs, 4)
	self:assertTypeErrorEquals(1, 'sparseIpairs', 'table', 'nil', tt.sparseIpairs)
end

------------------------------------------------------------------------
-- Test size function
------------------------------------------------------------------------

function suite:testSize()
	self:assertEquals(0, tt.size{})
	self:assertEquals(3, tt.size{foo = 'foo', bar = 'bar', baz = 'baz'})
	self:assertEquals(1, tt.size{1})
	self:assertEquals(5, tt.size{foo = 'foo', bar = 'bar', baz = 'baz', 1, 2})
	self:assertTypeErrorEquals(1, 'size', 'table', 'number', tt.size, 4)
	self:assertTypeErrorEquals(1, 'size', 'table', 'nil', tt.size)
end

------------------------------------------------------------------------
-- Test binaryLength function
------------------------------------------------------------------------

function suite:testLengthOfEmptyTableIsZero()
	self:assertEquals(0, tt.length{})
end

function suite:testLengthOfArray()
	local data = {"a","b","c","d","e","f"}
	self:assertEquals(6, tt.length(data))
	local meta = setmetatable({}, {__index = data})
	self:assertEquals(6, tt.length(meta))
end

function suite:testLengthOfQuasiArrayWithPrefixes()
	local data = {key1 = "value1", key2 = "value2", key3 = "value3"}
	self:assertEquals(3, tt.length(data, "key"))
	local meta = setmetatable({}, {__index = data})
	self:assertEquals(3, tt.length(meta, "key"))
end

function suite:testLengthChecksForTypeErrors()
	self:assertTypeErrorEquals(1, 'length', 'table', 'number', tt.length, 1)
	self:assertTypeErrorEquals(2, 'length', 'string', 'number', tt.length, {}, 2)
end

function suite:testInArray()
	self:assertTrue(tt.inArray({"a", "b", "c", "d"}, "b"))
	self:assertTrue(tt.inArray({"a", "b", "c", "d"}, "b", 0))
	self:assertTrue(tt.inArray({"a", "b", "c", "d"}, "b", 1))
	self:assertTrue(tt.inArray({"a", "b", "c", "d"}, "b", 2))
	self:assertTrue(tt.inArray({"a", "b", "c", "d"}, "b", -3))
	self:assertTrue(tt.inArray({"a", "b", "c", "d"}, "b", -5))
	self:assertTrue(tt.inArray({[1] = "a",[100] = "b",[101] = "c"}, "b"))
	self:assertTrue(tt.inArray({[1] = "a",[2] = "b",[3] = "c"}, "b", 0))
	self:assertTrue(tt.inArray({first = "a", second = "b", third = "c"}, "b"))
	self:assertFalse(tt.inArray({"a", "b", "c", "d"})) -- value missing
	self:assertFalse(tt.inArray({"a", "b", "c", "d"}, "e")) -- "e" is not in array
	self:assertFalse(tt.inArray({"a", "b", "c", "d"}, "b", 3)) -- "b" is before position 3
	self:assertFalse(tt.inArray({"a", "b", "c", "d"}, "b", 5)) -- 5 is larger than #array
	self:assertFalse(tt.inArray({"a", "b", "c", "d"}, "b", -2)) -- "b" is not in the last two positions
	self:assertFalse(tt.inArray({[1] = "a", [100] = "b", [101] = "c"}, "b", 0)) -- key 100 is non-consecutive
	self:assertFalse(tt.inArray({first = "a", second = "b", third = "c"}, "b", 0)) -- key "second" is not an integer
end

function suite:testMerge()
	self:assertDeepEquals({}, tt.merge())
	local function f() return "foo" end
	self:assertDeepEquals({"a",1,2,f,3}, tt.merge({"a",1,2,f,3}))
	self:assertDeepEquals({0.1,f,2,"3",{foo="bar"}}, tt.merge({0.1,f}, {[0.0]=f,2,"3",[1.0]="y"}, {{foo="bar"},f=f}))
	local function fn() tt.merge(2,3) end
	self:assertThrows(fn, "bad argument #1 to 'merge' (table expected, got number)")
end

function suite:testExtend()
	local t = {Z=1,{"Z"}}
	tt.extend(t, {[0.1]="k",-1})
	self:assertDeepEquals({{"Z"},Z=1,-1}, t)
	local function fn() tt.extend(t, "banana") end
	self:assertThrows(fn, "bad argument #2 to 'extend' (table expected, got string)")
	local function fn() tt.extend(fn, t) end
	self:assertThrows(fn, "bad argument #1 to 'extend' (table expected, got function)")
end

return suite
"https://si.wikibooks.org/w/index.php?title=Module:TableTools/testcases&oldid=28879" වෙතින් සම්ප්‍රවේශනය කෙරිණි