Jump to content

Module:TableTools/testcases

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Mr. Stradivarius (talk | contribs) at 14:35, 19 December 2013 (add removeDuplicates tests). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
-- Unit tests for [[Module:TableTools]]. Click talk page to run tests.

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

------------------------------------------------------------------------
-- 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'

------------------------------------------------------------------------
-- 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.arraysHaveSameValues(t1, t2)
	-- Checks whether two arrays contain the same values, not necessarily in the same order.
	-- Returns true if so, and false if not. Values that occur more than once will cause
	-- unexpected behaviour.
	if #t1 ~= #t2 then
		return false
	end
	for i1, v1 in ipairs(t1) do
		local isMatch = false
		for i2, v2 in ipairs(t2) do
			if v1 == v2 or suite.isNan(v1) and suite.isNan(v2) then
				isMatch = true
			end
		end
		if not isMatch then
			return false
		end
	end
	-- The above fails if there is more than one NaN, so count the total number of NaNs in each table.
	local t1Nans = 0
	for i1, v1 in ipairs(t1) do
		if suite.isNan(v1) then
			t1Nans = t1Nans + 1
		end
	end
	local t2Nans = 0
	for i2, v2 in ipairs(t2) do
		if suite.isNan(v2) then
			t2Nans = t2Nans + 1
		end
	end
	if t1Nans ~= t2Nans then
		return false
	end
	return true
end

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(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}
	-- Compressed: {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]))
end

------------------------------------------------------------------------
-- Union and intersection variables
------------------------------------------------------------------------

function suite.getKeyValueTables()
	local t1 = {eggs = 'eggs', beans = 'beans', sausage = 'sausage'}
	local t2 = {eggs = 'eggs', beans = 'baked beans', sausage = 'sausage', spam = 'spam'}
	local t3 = {lobster = 'lobster thermidor', eggs = 'scrambled eggs', beans = 'beans', sausage = 'sausage'}
	local union = {eggs = {'eggs', 'scrambled eggs'}, beans = {'beans', 'baked beans'}, sausage = 'sausage', spam = 'spam', lobster = 'lobster thermidor'}
	local intersection = {sausage = 'sausage'}
	local complement123 = {lobster = 'lobster thermidor', eggs = 'scrambled eggs'}
	local complement321 = {}
	return t1, t2, t3, union, intersection, complement123, complement321
end

function suite.getValueNumsTables()
	local nums1 = {1, 3, 4, 5, foo = 7}
	local nums2 = {2, bar = 3, 5, 6}
	local nums3 = {2, 3, 5, 8}
	local numsUnion = {1, 2, 3, 4, 5, 6, 7, 8}
	local numsIntersection = {3, 5}
	return nums1, nums2, nums3, numsUnion, numsIntersection
end

function suite.getValueStringsTables()
	local strings1 = {'foo', 'bar', 'baz'}
	local strings2 = {'eggs', 'sausage', 'spam', 'bar', 'baz'}
	local stringsUnion = {'foo', 'eggs', 'sausage', 'spam', 'bar', 'baz'}
	local stringsIntersection = {'bar', 'baz'}
	return strings1, strings2, stringsUnion, stringsIntersection
end

function suite.getValueMixedTables()
	local mixed1 = {1, 3, 4, 5, foo = 7, 'foo', 'bar', 'baz'}
	local mixed2 = {2, bar = 3, 5, 6, 'eggs', 'sausage', 'spam', 'bar', 'baz'}
	local mixedUnion = {1, 2, 3, 4, 5, 6, 7, 'foo', 'bar', 'baz', 'eggs', 'sausage', 'spam'}
	local mixedIntersection = {3, 5, 'bar', 'baz'}
	return mixed1, mixed2, mixedUnion, mixedIntersection
end

function suite.getValueNansTables()
	local nans1 = {1, 3, 0/0, 2}
	local nans2 = {5, 2, 3, 0/0, 6}
	local nansUnion = {1, 2, 3, 5, 6, 0/0, 0/0} -- We have two 0/0s here as NaNs are never equal to each other.
	local nansIntersection = {2, 3} -- 0/0 is not included as a NaN is never equal to another NaN.
	return nans1, nans2, nansUnion, nansIntersection
end

------------------------------------------------------------------------
-- Test union
------------------------------------------------------------------------

function suite:testUnion()
	local t1, t2, t3, union, intersection = suite.getKeyValueTables()
	self:assertDeepEquals(union, tt.union(t1, t2, t3))
	self:assertErrorEquals("no arguments passed to 'union'", tt.union)
	self:assertTypeErrorEquals(1, 'union', 'table', 'number', tt.union, 4)
	self:assertTypeErrorEquals(2, 'union', 'table', 'string', tt.union, {}, 'foo')
	self:assertTypeErrorEquals(1, 'union', 'table', 'nil', tt.union, nil)
end

------------------------------------------------------------------------
-- Test intersection
------------------------------------------------------------------------

function suite:testIntersection()
	local t1, t2, t3, union, intersection = suite.getKeyValueTables()
	self:assertDeepEquals(intersection, tt.intersection(t1, t2, t3))
	self:assertErrorEquals("no arguments passed to 'intersection'", tt.intersection)
	self:assertTypeErrorEquals(1, 'intersection', 'table', 'number', tt.intersection, 4)
	self:assertTypeErrorEquals(2, 'intersection', 'table', 'string', tt.intersection, {}, 'foo')
	self:assertTypeErrorEquals(1, 'intersection', 'table', 'nil', tt.intersection, nil)
end

------------------------------------------------------------------------
-- Test complement
------------------------------------------------------------------------

function suite:testComplement()
	local t1, t2, t3, union, intersection, complement123, complement321 = suite.getKeyValueTables()
	self:assertDeepEquals(complement123, tt.complement(t1, t2, t3))
	self:assertDeepEquals(complement321, tt.complement(t3, t2, t1))
	self:assertErrorEquals("no arguments passed to 'complement' (minimum is two)", tt.complement)
	self:assertErrorEquals("only one argument passed to 'complement' (minimum is two)", tt.complement, {})
	self:assertErrorEquals("only one argument passed to 'complement' (minimum is two)", tt.complement, 4)
	self:assertTypeErrorEquals(2, 'complement', 'table', 'string', tt.complement, {}, 'foo')
	self:assertTypeErrorEquals(3, 'complement', 'table', 'nil', tt.complement, {}, {}, nil, {})
end

------------------------------------------------------------------------
-- Test valueUnion
------------------------------------------------------------------------

function suite:testValueUnion()
	local nums1, nums2, nums3, numsUnion, numsIntersection = suite.getValueNumsTables()
	local numsUnionResult = tt.valueUnion(nums1, nums2, nums3)
	self:assertTrue(suite.arraysHaveSameValues(numsUnion, numsUnionResult))
	
	local strings1, strings2, stringsUnion, stringsIntersection = suite.getValueStringsTables()
	local stringsUnionResult = tt.valueUnion(strings1, strings2)
	self:assertTrue(suite.arraysHaveSameValues(stringsUnion, stringsUnionResult))
	
	local mixed1, mixed2, mixedUnion, mixedIntersection = suite.getValueMixedTables()
	local mixedUnionResult = tt.valueUnion(mixed1, mixed2)
	self:assertTrue(suite.arraysHaveSameValues(mixedUnion, mixedUnionResult))
	
	local nans1, nans2, nansUnion, nansIntersection = suite.getValueNansTables()
	local nansUnionResult = tt.valueUnion(nans1, nans2)
	self:assertTrue(suite.arraysHaveSameValues(nansUnion, nansUnionResult))

	self:assertErrorEquals("no arguments passed to 'valueUnion'", tt.valueUnion)
	self:assertTypeErrorEquals(1, 'valueUnion', 'table', 'number', tt.valueUnion, 4)
	self:assertTypeErrorEquals(2, 'valueUnion', 'table', 'string', tt.valueUnion, {}, 'foo')
	self:assertTypeErrorEquals(1, 'valueUnion', 'table', 'nil', tt.valueUnion, nil)
end

------------------------------------------------------------------------
-- Test valueIntersection
------------------------------------------------------------------------

function suite:testValueIntersection()
	local nums1, nums2, nums3, numsUnion, numsIntersection = suite.getValueNumsTables()
	local numsIntersectionResult = tt.valueIntersection(nums1, nums2, nums3)
	self:assertTrue(suite.arraysHaveSameValues(numsIntersection, numsIntersectionResult))
	
	local strings1, strings2, stringsUnion, stringsIntersection = suite.getValueStringsTables()	
	local stringsIntersectionResult = tt.valueIntersection(strings1, strings2)
	self:assertTrue(suite.arraysHaveSameValues(stringsIntersection, stringsIntersectionResult))
	
	local mixed1, mixed2, mixedUnion, mixedIntersection = suite.getValueMixedTables()
	local mixedIntersectionResult = tt.valueIntersection(mixed1, mixed2)
	self:assertTrue(suite.arraysHaveSameValues(mixedIntersection, mixedIntersectionResult))
	
	local nans1, nans2, nansUnion, nansIntersection = suite.getValueNansTables()
	local nansIntersectionResult = tt.valueIntersection(nans1, nans2)
	self:assertTrue(suite.arraysHaveSameValues(nansIntersection, nansIntersectionResult))

	self:assertErrorEquals("0 arguments passed to 'valueIntersection' (minimum is 2)", tt.valueIntersection)
	self:assertErrorEquals("1 argument passed to 'valueIntersection' (minimum is 2)", tt.valueIntersection, {})
	self:assertTypeErrorEquals(1, 'valueIntersection', 'table', 'number', tt.valueIntersection, 4, 5)
	self:assertTypeErrorEquals(2, 'valueIntersection', 'table', 'string', tt.valueIntersection, {}, 'foo')
	self:assertTypeErrorEquals(1, 'valueIntersection', 'table', 'nil', tt.valueIntersection, nil, nil)
end

------------------------------------------------------------------------
-- 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}
local aNumsConcatenated = '1310'
local aValsConcatenated = 'a1a3a10'
local bNumsConcatenated = '25'
local bValsConcatenated = 'b2b5'
local cdNumsConcatenated = '48'
local cdValsConcatenated = 'c4dc8d'

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')
    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:assertTypeErrorEquals(1, 'affixNums', 'table', 'number', tt.affixNums, 4)
	self:assertTypeErrorEquals(1, 'affixNums', 'table', 'nil', tt.affixNums)
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

return suite