Difference between revisions of "Module:Arguments/testcases"

From blackwiki
Jump to navigation Jump to search
blackwiki>Mr. Stradivarius
(add blank removal tests)
m (63 revisions imported)
 
(43 intermediate revisions by 3 users not shown)
Line 1: Line 1:
local getArgs = require('Module:Arguments').getArgs
+
local getArgs = require('Module:Arguments/sandbox').getArgs
 
local ScribuntoUnit = require('Module:ScribuntoUnit')
 
local ScribuntoUnit = require('Module:ScribuntoUnit')
 
local suite = ScribuntoUnit:new()
 
local suite = ScribuntoUnit:new()
  
-- Constants
+
--------------------------------------------------------------------------
 +
-- Default values
 +
--------------------------------------------------------------------------
 +
 
 +
local d = {}
 +
d.frameTitle = 'Frame title'
 +
d.parentTitle = 'Parent title'
 +
 
 +
-- Precedence-testing values
 +
d.firstFrameArg = 'first frame argument'
 +
d.firstParentArg = 'first parent argument'
 +
d.secondParentArg = 'second parent argument'
 +
d.uniqueFrameArg = 'unique frame argument'
 +
d.uniqueFrameArgKey = 'uniqueFrameArgKey'
 +
d.uniqueParentArg = 'unique parent argument'
 +
d.uniqueParentArgKey = 'uniqueParentArgKey'
  
local defaultArgs = {}
+
-- Trimming and whitespace values.
defaultArgs.invoke = 'firstInvokeArg'
+
-- Whitespace gets trimmed from named parameters, so keys for these need
defaultArgs[1] = 'firstTemplateArg'
+
-- to be numbers to make this a proper test.
defaultArgs[2] = 'secondTemplateArg'
+
d.blankArg = ''
local nilArg = 'nilArg'
+
d.blankArgKey = 100
 +
d.spacesArg = '\n  '
 +
d.spacesArgKey = 101
 +
d.untrimmedArg = '\n  foo bar  '
 +
d.untrimmedArgKey = 102
 +
d.trimmedArg = 'foo bar'
 +
d.valueFuncValue = 'valueFuncValue'
 +
d.defaultValueFunc = function() return d.valueFuncValue end
 +
d.translate = {
 +
foo = 'F00',
 +
bar = '8@r',
 +
baz = '8@z',
 +
qux = 'qUx'
 +
}
  
 
--------------------------------------------------------------------------
 
--------------------------------------------------------------------------
Line 15: Line 43:
 
--------------------------------------------------------------------------
 
--------------------------------------------------------------------------
  
------------------ Template invocations used by the module --------------------
+
function suite.getFrames(frameTitle, frameArgs, parentTitle, parentArgs)
-- Module talk:Arguments/testInvoke:
+
frameTitle = frameTitle or d.frameTitle
-- {{#invoke:arguments/testcases|{{{function|}}}|firstInvokeArg}}
+
frameArgs = frameArgs or {
-- Module talk:Arguments/testTemplate:
+
d.firstFrameArg,
-- {{Module talk:Arguments/testInvoke|{{{1|}}}|{{{2|}}}|blank=|function={{{function|}}}}}
+
[d.uniqueFrameArgKey] = d.uniqueFrameArg,
 +
[d.blankArgKey] = d.blankArg,
 +
[d.spacesArgKey] = d.spacesArg,
 +
[d.untrimmedArgKey] = d.untrimmedArg
 +
}
 +
parentTitle = parentTitle or d.parentTitle
 +
parentArgs = parentArgs or {
 +
d.firstParentArg,
 +
d.secondParentArg,
 +
[d.uniqueParentArgKey] = d.uniqueParentArg
 +
}
 +
local currentFrame = mw.getCurrentFrame()
 +
local parent = currentFrame:newChild{title = parentTitle, args = parentArgs}
 +
local frame = parent:newChild{title = frameTitle, args = frameArgs}
 +
return frame, parent
 +
end
 +
 
 +
function suite.getDefaultArgs(options, frameTitle, frameArgs, parentTitle, parentArgs)
 +
local frame, parent = suite.getFrames(frameTitle, frameArgs, parentTitle, parentArgs)
 +
local args = getArgs(frame, options)
 +
return args
 +
end
 +
 
 +
function suite:assertError(func, ...)
 +
-- Asserts that executing the function func results in an error.
 +
-- Parameters after func are func's arguments.
 +
local success, msg = pcall(func, ...)
 +
self:assertFalse(success)
 +
end
 +
 
 +
function suite:assertNumberOfIterations(expected, iterator, t)
 +
local noIterations = 0
 +
for k, v in iterator(t) do
 +
noIterations = noIterations + 1
 +
end
 +
self:assertEquals(expected, noIterations)
 +
end
 +
 
 +
--------------------------------------------------------------------------
 +
-- Test precedence
 +
--------------------------------------------------------------------------
 +
 
 +
function suite:assertDefaultPrecedence(args)
 +
self:assertEquals(d.firstFrameArg, args[1])
 +
self:assertEquals(d.secondParentArg, args[2])
 +
self:assertEquals(d.uniqueFrameArg, args[d.uniqueFrameArgKey])
 +
self:assertEquals(d.uniqueParentArg, args[d.uniqueParentArgKey])
 +
end
 +
 
 +
function suite:testDefaultPrecedence()
 +
self:assertDefaultPrecedence(suite.getDefaultArgs())
 +
end
 +
 
 +
function suite:testDefaultPrecedenceThroughWrapper()
 +
self:assertDefaultPrecedence(suite.getDefaultArgs{wrappers = {d.parentTitle}, parentOnly = false})
 +
end
 +
 
 +
function suite:testDefaultPrecedenceThroughNonWrapper()
 +
self:assertDefaultPrecedence(suite.getDefaultArgs({wrappers = d.parentTitle, frameOnly = false}, nil, nil, 'Not the parent title'))
 +
end
 +
 
 +
function suite:assertParentFirst(args)
 +
self:assertEquals(d.firstParentArg, args[1])
 +
self:assertEquals(d.secondParentArg, args[2])
 +
self:assertEquals(d.uniqueFrameArg, args[d.uniqueFrameArgKey])
 +
self:assertEquals(d.uniqueParentArg, args[d.uniqueParentArgKey])
 +
end
 +
 
 +
function suite:testParentFirst()
 +
self:assertParentFirst(suite.getDefaultArgs{parentFirst = true})
 +
end
 +
 
 +
function suite:testParentFirstThroughWrapper()
 +
self:assertParentFirst(suite.getDefaultArgs{wrappers = {d.parentTitle}, parentOnly = false, parentFirst = true})
 +
end
 +
 
 +
function suite:testParentFirstThroughNonWrapper()
 +
self:assertParentFirst(suite.getDefaultArgs({wrappers = d.parentTitle, frameOnly = false, parentFirst = true}, nil, nil, 'Not the parent title'))
 +
end
 +
 
 +
function suite:assertParentOnly(args)
 +
self:assertEquals(d.firstParentArg, args[1])
 +
self:assertEquals(d.secondParentArg, args[2])
 +
self:assertEquals(nil, args[d.uniqueFrameArgKey])
 +
self:assertEquals(d.uniqueParentArg, args[d.uniqueParentArgKey])
 +
end
  
function suite.expandTestTemplate(funcName, para1, para2)
+
function suite:testParentOnly()
local frame = mw.getCurrentFrame()
+
self:assertParentOnly(suite.getDefaultArgs{parentOnly = true})
local val = frame:preprocess('{{Module talk:Arguments/testTemplate|' .. (para1 or '') .. '|' .. (para2 or '') .. '|function=' .. funcName .. '}}')
 
return val
 
 
end
 
end
  
function suite.getDefaultTemplateExpansion(funcName)
+
function suite:testParentOnlyThroughWrapper()
return suite.expandTestTemplate(funcName, defaultArgs[1], defaultArgs[2])
+
self:assertParentOnly(suite.getDefaultArgs{wrappers = {d.parentTitle}})
 
end
 
end
  
function suite.getInvocation(funcName)
+
function suite:testParentOnlyThroughSandboxWrapper()
local frame = mw.getCurrentFrame()
+
self:assertParentOnly(suite.getDefaultArgs({wrappers = d.parentTitle}, nil, nil, d.parentTitle .. '/sandbox'))
local val = frame:preprocess('{{Module talk:Arguments/testInvoke|function=' .. funcName .. '}}')
 
return val
 
 
end
 
end
  
function suite.getDefaultExpansions(testcaseName)
+
function suite:assertFrameOnly(args)
local first = suite.getDefaultTemplateExpansion(testcaseName .. 'First')
+
self:assertEquals(d.firstFrameArg, args[1])
local second = suite.getDefaultTemplateExpansion(testcaseName .. 'Second')
+
self:assertEquals(nil, args[2])
local invoke = suite.getInvocation(testcaseName .. 'First')
+
self:assertEquals(d.uniqueFrameArg, args[d.uniqueFrameArgKey])
return first, second, invoke
+
self:assertEquals(nil, args[d.uniqueParentArgKey])
 
end
 
end
  
function suite.makeInvokeFunction(key, options)
+
function suite:testFrameOnly()
return function (frame)
+
self:assertFrameOnly(suite.getDefaultArgs{frameOnly = true})
options = options or {}
+
end
local args = getArgs(frame, options)
+
 
if args[key] ~= nil then
+
function suite:testFrameOnlyThroughNonWrapper()
return args[key]
+
self:assertFrameOnly(suite.getDefaultArgs({wrappers = d.parentTitle}, nil, nil, 'Not the parent title'))
else
 
return nilArg
 
end
 
end
 
 
end
 
end
  
function suite.makeArgInvokeFunctions(testcaseName, options)
+
function suite:testDefaultPrecedenceWithWhitespace()
suite[testcaseName .. 'First'] = suite.makeInvokeFunction(1, options)
+
local frame, parent = suite.getFrames(
suite[testcaseName .. 'Second'] = suite.makeInvokeFunction(2, options)
+
d.frameTitle,
 +
{' '},
 +
d.parentTitle,
 +
{d.firstParentArg}
 +
)
 +
local args = getArgs(frame)
 +
self:assertEquals(d.firstParentArg, args[1])
 
end
 
end
  
 
--------------------------------------------------------------------------
 
--------------------------------------------------------------------------
-- Test default precedence
+
-- Test trimming and blank removal
 
--------------------------------------------------------------------------
 
--------------------------------------------------------------------------
  
suite.makeArgInvokeFunctions('defaultPrecedence')
+
function suite:testDefaultTrimmingAndBlankRemoval()
 +
local args = suite.getDefaultArgs()
 +
self:assertEquals(nil, args[d.blankArgKey])
 +
self:assertEquals(nil, args[d.spacesArgKey])
 +
self:assertEquals(d.trimmedArg, args[d.untrimmedArgKey])
 +
end
  
function suite:testDefaultPrecedence()
+
function suite:testRemoveBlanksButNoTrimming()
local first, second, invoke = suite.getDefaultExpansions('defaultPrecedence')
+
local args = suite.getDefaultArgs{trim = false}
self:assertEquals(defaultArgs.invoke, first)
+
self:assertEquals(nil, args[d.blankArgKey])
self:assertEquals(defaultArgs[2], second)
+
self:assertEquals(nil, args[d.spacesArgKey])
self:assertEquals(defaultArgs.invoke, invoke)
+
self:assertEquals(d.untrimmedArg, args[d.untrimmedArgKey])
 +
end
 +
 
 +
function suite:testTrimButNoBlankRemoval()
 +
local args = suite.getDefaultArgs{removeBlanks = false}
 +
self:assertEquals(d.blankArg, args[d.blankArgKey])
 +
self:assertEquals('', args[d.spacesArgKey])
 +
self:assertEquals(d.trimmedArg, args[d.untrimmedArgKey])
 +
end
 +
 
 +
function suite:testNoTrimOrBlankRemoval()
 +
local args = suite.getDefaultArgs{trim = false, removeBlanks = false}
 +
self:assertEquals(d.blankArg, args[d.blankArgKey])
 +
self:assertEquals(d.spacesArg, args[d.spacesArgKey])
 +
self:assertEquals(d.untrimmedArg, args[d.untrimmedArgKey])
 
end
 
end
  
 
--------------------------------------------------------------------------
 
--------------------------------------------------------------------------
-- Test parent first
+
-- Test valueFunc
 
--------------------------------------------------------------------------
 
--------------------------------------------------------------------------
  
suite.makeArgInvokeFunctions('parentFirst', {parentFirst = true})
+
function suite:testValueFunc()
 +
local args = suite.getDefaultArgs{valueFunc = d.defaultValueFunc}
 +
self:assertEquals(d.valueFuncValue, args['some random key: sdfaliwyda'])
 +
end
 +
 
 +
function suite:testValueFuncPrecedence()
 +
local args = suite.getDefaultArgs{
 +
trim = false,
 +
removeBlanks = false,
 +
valueFunc = d.defaultValueFunc
 +
}
 +
self:assertEquals(d.valueFuncValue, args[1])
 +
self:assertEquals(d.valueFuncValue, args['some random key: gekjabawyvy'])
 +
end
 +
 
 +
function suite:testValueFuncKey()
 +
local args = suite.getDefaultArgs{valueFunc = function(key, value)
 +
return 'valueFunc key: '.. key
 +
end}
 +
self:assertEquals('valueFunc key: foo', args.foo)
 +
end
  
function suite:testParentFirst()
+
function suite:testValueFuncValue()
local first, second, invoke = suite.getDefaultExpansions('parentFirst')
+
local args = suite.getDefaultArgs{valueFunc = function(key, value)
self:assertEquals(defaultArgs[1], first)
+
return 'valueFunc value: '.. value
self:assertEquals(defaultArgs[2], second)
+
end}
self:assertEquals(defaultArgs.invoke, invoke)
+
self:assertEquals(
 +
'valueFunc value: ' .. d.uniqueFrameArg,
 +
args[d.uniqueFrameArgKey]
 +
)
 
end
 
end
  
 
--------------------------------------------------------------------------
 
--------------------------------------------------------------------------
-- Test parent only
+
-- Test adding new arguments
 
--------------------------------------------------------------------------
 
--------------------------------------------------------------------------
  
suite.makeArgInvokeFunctions('parentOnly', {parentOnly = true})
+
function suite:testAddingNewArgs()
 +
local args = suite.getDefaultArgs()
 +
self:assertEquals(nil, args.newKey)
 +
args.newKey = 'some new key'
 +
self:assertEquals('some new key', args.newKey)
 +
end
 +
 
 +
function suite:testAddingNewBlankArgs()
 +
local args = suite.getDefaultArgs()
 +
self:assertEquals(nil, args.newKey)
 +
args.newKey = ''
 +
self:assertEquals('', args.newKey)
 +
end
 +
 
 +
function suite:testAddingNewSpacesArgs()
 +
local args = suite.getDefaultArgs()
 +
self:assertEquals(nil, args.newKey)
 +
args.newKey = ' '
 +
self:assertEquals(' ', args.newKey)
 +
end
 +
 
 +
function suite:testOverwriting()
 +
local args = suite.getDefaultArgs()
 +
self:assertEquals(d.firstFrameArg, args[1])
 +
args[1] = 'a new first frame argument'
 +
self:assertEquals('a new first frame argument', args[1])
 +
end
 +
 
 +
function suite:testOverwritingWithNil()
 +
local args = suite.getDefaultArgs()
 +
self:assertEquals(d.firstFrameArg, args[1])
 +
args[1] = nil
 +
self:assertEquals(nil, args[1])
 +
end
 +
 
 +
function suite:testOverwritingWithBlank()
 +
local args = suite.getDefaultArgs()
 +
self:assertEquals(d.firstFrameArg, args[1])
 +
args[1] = ''
 +
self:assertEquals('', args[1])
 +
end
 +
 
 +
function suite:testOverwritingWithSpaces()
 +
local args = suite.getDefaultArgs()
 +
self:assertEquals(d.firstFrameArg, args[1])
 +
args[1] = ' '
 +
self:assertEquals(' ', args[1])
 +
end
  
function suite:testParentOnly()
+
function suite:testReadOnly()
local first, second, invoke = suite.getDefaultExpansions('parentOnly')
+
local args = suite.getDefaultArgs{readOnly = true}
self:assertEquals(defaultArgs[1], first)
+
local function testFunc()
self:assertEquals(defaultArgs[2], second)
+
args.newKey = 'some new value'
self:assertEquals(nilArg, invoke)
+
end
 +
self:assertError(testFunc)
 +
end
 +
 
 +
function suite:testNoOverwriteExistingKey()
 +
local args = suite.getDefaultArgs{noOverwrite = true}
 +
self:assertEquals(d.firstFrameArg, args[1])
 +
local function testFunc()
 +
args[1] = 'a new first frame argument'
 +
end
 +
self:assertError(testFunc)
 +
end
 +
 
 +
function suite:testNoOverwriteNewKey()
 +
local args = suite.getDefaultArgs{noOverwrite = true}
 +
self:assertEquals(nil, args.newKey)
 +
args.newKey = 'some new value'
 +
self:assertEquals('some new value', args.newKey)
 
end
 
end
  
 
--------------------------------------------------------------------------
 
--------------------------------------------------------------------------
-- Test frame only
+
-- Test bad input
 
--------------------------------------------------------------------------
 
--------------------------------------------------------------------------
  
suite.makeArgInvokeFunctions('frameOnly', {frameOnly = true})
+
function suite:testBadFrameInput()
 +
self:assertError(getArgs, 'foo')
 +
self:assertError(getArgs, 9)
 +
self:assertError(getArgs, true)
 +
self:assertError(getArgs, function() return true end)
 +
end
 +
 
 +
function suite:testBadOptionsInput()
 +
self:assertError(getArgs, {}, 'foo')
 +
self:assertError(getArgs, {}, 9)
 +
self:assertError(getArgs, {}, true)
 +
self:assertError(getArgs, {}, function() return true end)
 +
end
  
function suite:testFrameOnly()
+
function suite:testBadValueFuncInput()
local first, second, invoke = suite.getDefaultExpansions('frameOnly')
+
self:assertError(getArgs, {}, {valueFunc = 'foo'})
self:assertEquals(defaultArgs.invoke, first)
+
self:assertError(getArgs, {}, {valueFunc = 9})
self:assertEquals(nilArg, second)
+
self:assertError(getArgs, {}, {valueFunc = true})
self:assertEquals(defaultArgs.invoke, invoke)
+
self:assertError(getArgs, {}, {valueFunc = {}})
 
end
 
end
  
 
--------------------------------------------------------------------------
 
--------------------------------------------------------------------------
-- Test default trimming
+
-- Test iterator metamethods
 
--------------------------------------------------------------------------
 
--------------------------------------------------------------------------
  
suite.defaultTrimming = suite.makeInvokeFunction(2)
+
function suite:testPairs()
 +
local args = getArgs{'foo', 'bar', baz = 'qux'}
 +
self:assertNumberOfIterations(3, pairs, args)
 +
end
 +
 
 +
function suite:testIpairs()
 +
local args = getArgs{'foo', 'bar', baz = 'qux'}
 +
self:assertNumberOfIterations(2, ipairs, args)
 +
end
 +
 
 +
function suite:testNoNilsinPairs()
 +
-- Test that when we use pairs, we don't iterate over any nil values
 +
-- that have been memoized.
 +
local args = getArgs{''}
 +
local temp = args[1] -- Memoizes the nil
 +
self:assertNumberOfIterations(0, pairs, args)
 +
end
 +
 
 +
function suite:testNoNilsinIpairs()
 +
-- Test that when we use ipairs, we don't iterate over any nil values
 +
-- that have been memoized.
 +
local args = getArgs{''}
 +
local temp = args[1] -- Memoizes the nil
 +
self:assertNumberOfIterations(0, ipairs, args)
 +
end
 +
 
 +
function suite:testDeletedArgsInPairs()
 +
-- Test that when we use pairs, we don't iterate over any values that have
 +
-- been explicitly set to nil.
 +
local args = getArgs{'foo'}
 +
args[1] = nil
 +
self:assertNumberOfIterations(0, pairs, args)
 +
end
 +
 
 +
function suite:testDeletedArgsInIpairs()
 +
-- Test that when we use ipairs, we don't iterate over any values that have
 +
-- been explicitly set to nil.
 +
local args = getArgs{'foo'}
 +
args[1] = nil
 +
self:assertNumberOfIterations(0, ipairs, args)
 +
end
  
function suite:testDefaultTrimming()
+
function suite:testNoNilsInPairsAfterIndex()
local arg = suite.expandTestTemplate('defaultTrimming', nil, '  argWithPadding  ')
+
-- Test that when we use pairs, we don't iterate over any nils that
self:assertEquals('argWithPadding', arg)
+
-- might have been memoized after a value that is not present in the
 +
-- original args table is indexed.
 +
local args = getArgs{}
 +
local temp = args.someRandomValue -- Memoizes the nil
 +
self:assertNumberOfIterations(0, pairs, args)
 
end
 
end
  
--------------------------------------------------------------------------
+
function suite:testNoNilsInPairsAfterNewindex()
-- Test no trimming
+
-- Test that when we use pairs, we don't iterate over any nils that
--------------------------------------------------------------------------
+
-- might have been memoized after a value that is not present in the
 +
-- original args table is added to the args table.
 +
local args = getArgs{}
 +
args.newKey = nil -- The nil is memoized
 +
self:assertNumberOfIterations(0, pairs, args)
 +
end
  
suite.noTrimming = suite.makeInvokeFunction(2, {trim = false})
+
function suite:testNoTableLengthChangeWhileIterating()
 +
-- Test that the number of arguments doesn't change if we index the
 +
-- args table while iterating.
 +
-- (Note that the equivalent test is not needed for new arg table
 +
-- indexes, as that would be a user error - doing so produces
 +
-- undetermined behaviour in Lua's next() function.)
 +
local args = getArgs{'foo', 'bar', baz = 'qux'}
 +
self:assertNumberOfIterations(3, pairs, args)
 +
for k, v in pairs(args) do
 +
local temp = args[k .. 'foo']
 +
end
 +
self:assertNumberOfIterations(3, pairs, args)
 +
end
  
function suite:testNoTrimming()
+
function suite:testPairsPrecedenceWithWhitespace()
local arg = suite.expandTestTemplate('noTrimming', nil, '  argWithPadding  ')
+
local frame, parent = suite.getFrames(
self:assertEquals(' argWithPadding  ', arg)
+
d.frameTitle,
 +
{' '},
 +
d.parentTitle,
 +
{d.firstParentArg}
 +
)
 +
local args = getArgs(frame)
 +
local actual
 +
for k, v in pairs(args) do
 +
actual = v
 +
end
 +
self:assertEquals(d.firstParentArg, actual)
 +
-- Check that we have actually iterated.
 +
self:assertNumberOfIterations(1, pairs, args)
 +
end
 +
 
 +
function suite:testPairsPrecedenceWithNil()
 +
local frame, parent = suite.getFrames(
 +
d.frameTitle,
 +
{},
 +
d.parentTitle,
 +
{d.firstParentArg}
 +
)
 +
local args = getArgs(frame)
 +
local actual
 +
for k, v in pairs(args) do
 +
actual = v
 +
end
 +
self:assertEquals(d.firstParentArg, actual)
 +
-- Check that we have actually iterated.
 +
self:assertNumberOfIterations(1, pairs, args)
 +
end
 +
 
 +
function suite:testIpairsEarlyExit()
 +
local mt = {}
 +
function mt.__index(t, k)
 +
if k == 1 then
 +
return 'foo'
 +
elseif k == 2 then
 +
return 'bar'
 +
elseif k == 3 then
 +
error('Expanded argument 3 unnecessarily')
 +
end
 +
end
 +
function mt.__pairs(t)
 +
error('Called pairs unnecessarily')
 +
end
 +
function mt.__ipairs(t)
 +
-- Works just like the default ipairs, except respecting __index
 +
return function(t, i)
 +
local v = t[i + 1]
 +
if v ~= nil then
 +
return i + 1, v
 +
end
 +
end, t, 0
 +
end
 +
local args = getArgs(setmetatable({}, mt))
 +
for k,v in ipairs(args) do
 +
if v == 'bar' then
 +
break
 +
end
 +
end
 
end
 
end
  
 
--------------------------------------------------------------------------
 
--------------------------------------------------------------------------
-- Test default blank removal
+
-- Test argument translation
 
--------------------------------------------------------------------------
 
--------------------------------------------------------------------------
  
suite.defaultBlankRemoval = suite.makeInvokeFunction(2)
+
function suite:testTranslationIndex()
 +
local args = getArgs({F00 = 'one', ['8@r'] = 'two', ['8@z'] = 'three', qUx = 'four', foo = 'nope', untranslated = 'yep'}, {translate = d.translate})
 +
self:assertEquals('one', args.foo)
 +
self:assertEquals('two', args.bar)
 +
self:assertEquals('three', args.baz)
 +
self:assertEquals('four', args.qux)
 +
self:assertEquals('yep', args.untranslated)
 +
end
 +
 
 +
function suite:testTranslationPairsWithAutoBacktranslate()
 +
local args = getArgs({F00 = 'one', ['8@r'] = 'two', ['8@z'] = 'three', qUx = 'four', foo = 'nope', untranslated = 'yep'}, {translate = d.translate})
 +
local cleanArgs = {}
 +
for k,v in pairs(args) do
 +
cleanArgs[k] = v
 +
end
 +
self:assertDeepEquals(
 +
{
 +
foo = 'one',
 +
bar = 'two',
 +
baz = 'three',
 +
qux = 'four',
 +
untranslated = 'yep'
 +
},
 +
cleanArgs
 +
)
 +
end
  
function suite:testDefaultBlankRemoval()
+
function suite:testTranslationPairsWithBacktranslate()
local arg = suite.expandTestTemplate('defaultBlankRemoval', nil, '')
+
local args = getArgs({F00 = 'one', ['8@r'] = 'two', ['8@z'] = 'three', qUx = 'four', foo = 'nope', untranslated = 'yep'}, {translate = d.translate, backtranslate = {F00 = 'foo'}})
self:assertEquals(nilArg, arg)
+
local cleanArgs = {}
 +
for k,v in pairs(args) do
 +
cleanArgs[k] = v
 +
end
 +
self:assertDeepEquals(
 +
{
 +
foo = 'one',
 +
['8@r'] = 'two',
 +
['8@z'] = 'three',
 +
qUx = 'four',
 +
untranslated = 'yep'
 +
},
 +
cleanArgs
 +
)
 
end
 
end
  
--------------------------------------------------------------------------
+
function suite:testTranslationPairsWithoutBacktranslate()
-- Test no blank removal
+
local args = getArgs({F00 = 'one', ['8@r'] = 'two', ['8@z'] = 'three', qUx = 'four', foo = 'nope', untranslated = 'yep'}, {translate = d.translate, backtranslate = false})
--------------------------------------------------------------------------
+
local cleanArgs = {}
 +
for k,v in pairs(args) do
 +
cleanArgs[k] = v
 +
end
 +
self:assertDeepEquals(
 +
{
 +
F00 = 'one',
 +
['8@r'] = 'two',
 +
['8@z'] = 'three',
 +
qUx = 'four',
 +
foo = 'nope',
 +
untranslated = 'yep'
 +
},
 +
cleanArgs
 +
)
 +
end
  
suite.noBlankRemoval = suite.makeInvokeFunction(2, {removeBlanks = false})
+
function suite:testTranslationNewindex()
 +
local args = getArgs({F00 = 'one', ['8@r'] = 'two', ['8@z'] = 'three', qUx = 'four', foo = 'nope', untranslated = 'yep'}, {translate = d.translate, backtranslate = false})
 +
args.foo = 'changed1'
 +
args.untranslated = 'changed2'
 +
local cleanArgs = {}
 +
for k,v in pairs(args) do
 +
cleanArgs[k] = v
 +
end
 +
self:assertDeepEquals(
 +
{
 +
F00 = 'changed1',
 +
['8@r'] = 'two',
 +
['8@z'] = 'three',
 +
qUx = 'four',
 +
foo = 'nope',
 +
untranslated = 'changed2'
 +
},
 +
cleanArgs
 +
)
 +
end
  
function suite:testNoBlankRemoval()
+
function suite:test_argument()
local arg = suite.expandTestTemplate('noBlankRemoval', nil, '')
+
local currentFrame = mw.getCurrentFrame()
self:assertEquals('', arg)
+
currentFrame.args[5] = 555;
 +
local args = getArgs(currentFrame)
 +
self:assertEquals('nil', type(args.foo))
 
end
 
end
  
 
return suite
 
return suite

Latest revision as of 13:10, 26 September 2020

Documentation for this module may be created at Module:Arguments/testcases/doc

local getArgs = require('Module:Arguments/sandbox').getArgs
local ScribuntoUnit = require('Module:ScribuntoUnit')
local suite = ScribuntoUnit:new()

--------------------------------------------------------------------------
-- Default values
--------------------------------------------------------------------------

local d = {} 
d.frameTitle = 'Frame title'
d.parentTitle = 'Parent title'

-- Precedence-testing values
d.firstFrameArg = 'first frame argument'
d.firstParentArg = 'first parent argument'
d.secondParentArg = 'second parent argument'
d.uniqueFrameArg = 'unique frame argument'
d.uniqueFrameArgKey = 'uniqueFrameArgKey'
d.uniqueParentArg = 'unique parent argument'
d.uniqueParentArgKey = 'uniqueParentArgKey'

-- Trimming and whitespace values.
-- Whitespace gets trimmed from named parameters, so keys for these need
-- to be numbers to make this a proper test.
d.blankArg = ''
d.blankArgKey = 100 
d.spacesArg = '\n   '
d.spacesArgKey = 101
d.untrimmedArg = '\n   foo bar   '
d.untrimmedArgKey = 102
d.trimmedArg = 'foo bar'
d.valueFuncValue = 'valueFuncValue'
d.defaultValueFunc = function() return d.valueFuncValue end
d.translate = {
	foo = 'F00',
	bar = '8@r',
	baz = '8@z',
	qux = 'qUx'
}

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

function suite.getFrames(frameTitle, frameArgs, parentTitle, parentArgs)
	frameTitle = frameTitle or d.frameTitle
	frameArgs = frameArgs or {
		d.firstFrameArg,
		[d.uniqueFrameArgKey] = d.uniqueFrameArg,
		[d.blankArgKey] = d.blankArg,
		[d.spacesArgKey] = d.spacesArg,
		[d.untrimmedArgKey] = d.untrimmedArg
	}
	parentTitle = parentTitle or d.parentTitle
	parentArgs = parentArgs or {
		d.firstParentArg,
		d.secondParentArg,
		[d.uniqueParentArgKey] = d.uniqueParentArg
	}
	local currentFrame = mw.getCurrentFrame()
	local parent = currentFrame:newChild{title = parentTitle, args = parentArgs}
	local frame = parent:newChild{title = frameTitle, args = frameArgs}
	return frame, parent
end

function suite.getDefaultArgs(options, frameTitle, frameArgs, parentTitle, parentArgs)
	local frame, parent = suite.getFrames(frameTitle, frameArgs, parentTitle, parentArgs)
	local args = getArgs(frame, options)
	return args
end

function suite:assertError(func, ...)
	-- Asserts that executing the function func results in an error.
	-- Parameters after func are func's arguments.
	local success, msg = pcall(func, ...)
	self:assertFalse(success)
end

function suite:assertNumberOfIterations(expected, iterator, t)
	local noIterations = 0
	for k, v in iterator(t) do
		noIterations = noIterations + 1
	end
	self:assertEquals(expected, noIterations)
end

--------------------------------------------------------------------------
-- Test precedence
--------------------------------------------------------------------------

function suite:assertDefaultPrecedence(args)
	self:assertEquals(d.firstFrameArg, args[1])
	self:assertEquals(d.secondParentArg, args[2])
	self:assertEquals(d.uniqueFrameArg, args[d.uniqueFrameArgKey])
	self:assertEquals(d.uniqueParentArg, args[d.uniqueParentArgKey])
end

function suite:testDefaultPrecedence()
	self:assertDefaultPrecedence(suite.getDefaultArgs())
end

function suite:testDefaultPrecedenceThroughWrapper()
	self:assertDefaultPrecedence(suite.getDefaultArgs{wrappers = {d.parentTitle}, parentOnly = false})
end

function suite:testDefaultPrecedenceThroughNonWrapper()
	self:assertDefaultPrecedence(suite.getDefaultArgs({wrappers = d.parentTitle, frameOnly = false}, nil, nil, 'Not the parent title'))
end

function suite:assertParentFirst(args)
	self:assertEquals(d.firstParentArg, args[1])
	self:assertEquals(d.secondParentArg, args[2])
	self:assertEquals(d.uniqueFrameArg, args[d.uniqueFrameArgKey])
	self:assertEquals(d.uniqueParentArg, args[d.uniqueParentArgKey])
end

function suite:testParentFirst()
	self:assertParentFirst(suite.getDefaultArgs{parentFirst = true})
end

function suite:testParentFirstThroughWrapper()
	self:assertParentFirst(suite.getDefaultArgs{wrappers = {d.parentTitle}, parentOnly = false, parentFirst = true})
end

function suite:testParentFirstThroughNonWrapper()
	self:assertParentFirst(suite.getDefaultArgs({wrappers = d.parentTitle, frameOnly = false, parentFirst = true}, nil, nil, 'Not the parent title'))
end

function suite:assertParentOnly(args)
	self:assertEquals(d.firstParentArg, args[1])
	self:assertEquals(d.secondParentArg, args[2])
	self:assertEquals(nil, args[d.uniqueFrameArgKey])
	self:assertEquals(d.uniqueParentArg, args[d.uniqueParentArgKey])
end

function suite:testParentOnly()
	self:assertParentOnly(suite.getDefaultArgs{parentOnly = true})
end

function suite:testParentOnlyThroughWrapper()
	self:assertParentOnly(suite.getDefaultArgs{wrappers = {d.parentTitle}})
end

function suite:testParentOnlyThroughSandboxWrapper()
	self:assertParentOnly(suite.getDefaultArgs({wrappers = d.parentTitle}, nil, nil, d.parentTitle .. '/sandbox'))
end

function suite:assertFrameOnly(args)
	self:assertEquals(d.firstFrameArg, args[1])
	self:assertEquals(nil, args[2])
	self:assertEquals(d.uniqueFrameArg, args[d.uniqueFrameArgKey])
	self:assertEquals(nil, args[d.uniqueParentArgKey])
end

function suite:testFrameOnly()
	self:assertFrameOnly(suite.getDefaultArgs{frameOnly = true})
end

function suite:testFrameOnlyThroughNonWrapper()
	self:assertFrameOnly(suite.getDefaultArgs({wrappers = d.parentTitle}, nil, nil, 'Not the parent title'))
end

function suite:testDefaultPrecedenceWithWhitespace()
	local frame, parent = suite.getFrames(
		d.frameTitle,
		{'  '},
		d.parentTitle,
		{d.firstParentArg}
	)
	local args = getArgs(frame)
	self:assertEquals(d.firstParentArg, args[1])
end

--------------------------------------------------------------------------
-- Test trimming and blank removal
--------------------------------------------------------------------------

function suite:testDefaultTrimmingAndBlankRemoval()
	local args = suite.getDefaultArgs()
	self:assertEquals(nil, args[d.blankArgKey])
	self:assertEquals(nil, args[d.spacesArgKey])
	self:assertEquals(d.trimmedArg, args[d.untrimmedArgKey])
end

function suite:testRemoveBlanksButNoTrimming()
	local args = suite.getDefaultArgs{trim = false}
	self:assertEquals(nil, args[d.blankArgKey])
	self:assertEquals(nil, args[d.spacesArgKey])
	self:assertEquals(d.untrimmedArg, args[d.untrimmedArgKey])
end

function suite:testTrimButNoBlankRemoval()
	local args = suite.getDefaultArgs{removeBlanks = false}
	self:assertEquals(d.blankArg, args[d.blankArgKey])
	self:assertEquals('', args[d.spacesArgKey])
	self:assertEquals(d.trimmedArg, args[d.untrimmedArgKey])
end

function suite:testNoTrimOrBlankRemoval()
	local args = suite.getDefaultArgs{trim = false, removeBlanks = false}
	self:assertEquals(d.blankArg, args[d.blankArgKey])
	self:assertEquals(d.spacesArg, args[d.spacesArgKey])
	self:assertEquals(d.untrimmedArg, args[d.untrimmedArgKey])
end

--------------------------------------------------------------------------
-- Test valueFunc
--------------------------------------------------------------------------

function suite:testValueFunc()
	local args = suite.getDefaultArgs{valueFunc = d.defaultValueFunc}
	self:assertEquals(d.valueFuncValue, args['some random key: sdfaliwyda'])
end

function suite:testValueFuncPrecedence()
	local args = suite.getDefaultArgs{
		trim = false,
		removeBlanks = false,
		valueFunc = d.defaultValueFunc
	}
	self:assertEquals(d.valueFuncValue, args[1])
	self:assertEquals(d.valueFuncValue, args['some random key: gekjabawyvy'])
end

function suite:testValueFuncKey()
	local args = suite.getDefaultArgs{valueFunc = function(key, value)
		return 'valueFunc key: '.. key
	end}
	self:assertEquals('valueFunc key: foo', args.foo)
end

function suite:testValueFuncValue()
	local args = suite.getDefaultArgs{valueFunc = function(key, value)
		return 'valueFunc value: '.. value
	end}
	self:assertEquals(
		'valueFunc value: ' .. d.uniqueFrameArg,
		args[d.uniqueFrameArgKey]
	)
end

--------------------------------------------------------------------------
-- Test adding new arguments
--------------------------------------------------------------------------

function suite:testAddingNewArgs()
	local args = suite.getDefaultArgs()
	self:assertEquals(nil, args.newKey)
	args.newKey = 'some new key'
	self:assertEquals('some new key', args.newKey)
end

function suite:testAddingNewBlankArgs()
	local args = suite.getDefaultArgs()
	self:assertEquals(nil, args.newKey)
	args.newKey = ''
	self:assertEquals('', args.newKey)
end

function suite:testAddingNewSpacesArgs()
	local args = suite.getDefaultArgs()
	self:assertEquals(nil, args.newKey)
	args.newKey = ' '
	self:assertEquals(' ', args.newKey)
end

function suite:testOverwriting()
	local args = suite.getDefaultArgs()
	self:assertEquals(d.firstFrameArg, args[1])
	args[1] = 'a new first frame argument'
	self:assertEquals('a new first frame argument', args[1])
end

function suite:testOverwritingWithNil()
	local args = suite.getDefaultArgs()
	self:assertEquals(d.firstFrameArg, args[1])
	args[1] = nil
	self:assertEquals(nil, args[1])
end

function suite:testOverwritingWithBlank()
	local args = suite.getDefaultArgs()
	self:assertEquals(d.firstFrameArg, args[1])
	args[1] = ''
	self:assertEquals('', args[1])
end

function suite:testOverwritingWithSpaces()
	local args = suite.getDefaultArgs()
	self:assertEquals(d.firstFrameArg, args[1])
	args[1] = ' '
	self:assertEquals(' ', args[1])
end

function suite:testReadOnly()
	local args = suite.getDefaultArgs{readOnly = true}
	local function testFunc()
		args.newKey = 'some new value'
	end
	self:assertError(testFunc)
end

function suite:testNoOverwriteExistingKey()
	local args = suite.getDefaultArgs{noOverwrite = true}
	self:assertEquals(d.firstFrameArg, args[1])
	local function testFunc()
		args[1] = 'a new first frame argument'
	end
	self:assertError(testFunc)
end

function suite:testNoOverwriteNewKey()
	local args = suite.getDefaultArgs{noOverwrite = true}
	self:assertEquals(nil, args.newKey)
	args.newKey = 'some new value'
	self:assertEquals('some new value', args.newKey)
end

--------------------------------------------------------------------------
-- Test bad input
--------------------------------------------------------------------------

function suite:testBadFrameInput()
	self:assertError(getArgs, 'foo')
	self:assertError(getArgs, 9)
	self:assertError(getArgs, true)
	self:assertError(getArgs, function() return true end)
end

function suite:testBadOptionsInput()
	self:assertError(getArgs, {}, 'foo')
	self:assertError(getArgs, {}, 9)
	self:assertError(getArgs, {}, true)
	self:assertError(getArgs, {}, function() return true end)
end

function suite:testBadValueFuncInput()
	self:assertError(getArgs, {}, {valueFunc = 'foo'})
	self:assertError(getArgs, {}, {valueFunc = 9})
	self:assertError(getArgs, {}, {valueFunc = true})
	self:assertError(getArgs, {}, {valueFunc = {}})
end

--------------------------------------------------------------------------
-- Test iterator metamethods
--------------------------------------------------------------------------

function suite:testPairs()
	local args = getArgs{'foo', 'bar', baz = 'qux'}
	self:assertNumberOfIterations(3, pairs, args)
end

function suite:testIpairs()
	local args = getArgs{'foo', 'bar', baz = 'qux'}
	self:assertNumberOfIterations(2, ipairs, args)
end

function suite:testNoNilsinPairs()
	-- Test that when we use pairs, we don't iterate over any nil values
	-- that have been memoized.
	local args = getArgs{''}
	local temp = args[1] -- Memoizes the nil
	self:assertNumberOfIterations(0, pairs, args)
end

function suite:testNoNilsinIpairs()
	-- Test that when we use ipairs, we don't iterate over any nil values
	-- that have been memoized.
	local args = getArgs{''}
	local temp = args[1] -- Memoizes the nil
	self:assertNumberOfIterations(0, ipairs, args)
end

function suite:testDeletedArgsInPairs()
	-- Test that when we use pairs, we don't iterate over any values that have
	-- been explicitly set to nil.
	local args = getArgs{'foo'}
	args[1] = nil
	self:assertNumberOfIterations(0, pairs, args)
end

function suite:testDeletedArgsInIpairs()
	-- Test that when we use ipairs, we don't iterate over any values that have
	-- been explicitly set to nil.
	local args = getArgs{'foo'}
	args[1] = nil
	self:assertNumberOfIterations(0, ipairs, args)
end

function suite:testNoNilsInPairsAfterIndex()
	-- Test that when we use pairs, we don't iterate over any nils that
	-- might have been memoized after a value that is not present in the
	-- original args table is indexed.
	local args = getArgs{}
	local temp = args.someRandomValue -- Memoizes the nil
	self:assertNumberOfIterations(0, pairs, args)
end

function suite:testNoNilsInPairsAfterNewindex()
	-- Test that when we use pairs, we don't iterate over any nils that
	-- might have been memoized after a value that is not present in the
	-- original args table is added to the args table.
	local args = getArgs{}
	args.newKey = nil -- The nil is memoized
	self:assertNumberOfIterations(0, pairs, args)
end

function suite:testNoTableLengthChangeWhileIterating()
	-- Test that the number of arguments doesn't change if we index the
	-- args table while iterating.
	-- (Note that the equivalent test is not needed for new arg table
	-- indexes, as that would be a user error - doing so produces
	-- undetermined behaviour in Lua's next() function.)
	local args = getArgs{'foo', 'bar', baz = 'qux'}
	self:assertNumberOfIterations(3, pairs, args)
	for k, v in pairs(args) do
		local temp = args[k .. 'foo']
	end
	self:assertNumberOfIterations(3, pairs, args)
end

function suite:testPairsPrecedenceWithWhitespace()
	local frame, parent = suite.getFrames(
		d.frameTitle,
		{'  '},
		d.parentTitle,
		{d.firstParentArg}
	)
	local args = getArgs(frame)
	local actual
	for k, v in pairs(args) do
		actual = v
	end
	self:assertEquals(d.firstParentArg, actual)
	-- Check that we have actually iterated.
	self:assertNumberOfIterations(1, pairs, args)
end

function suite:testPairsPrecedenceWithNil()
	local frame, parent = suite.getFrames(
		d.frameTitle,
		{},
		d.parentTitle,
		{d.firstParentArg}
	)
	local args = getArgs(frame)
	local actual
	for k, v in pairs(args) do
		actual = v
	end
	self:assertEquals(d.firstParentArg, actual)
	-- Check that we have actually iterated.
	self:assertNumberOfIterations(1, pairs, args)
end

function suite:testIpairsEarlyExit()
	local mt = {}
	function mt.__index(t, k)
		if k == 1 then
			return 'foo'
		elseif k == 2 then
			return 'bar'
		elseif k == 3 then
			error('Expanded argument 3 unnecessarily')
		end
	end
	function mt.__pairs(t)
		error('Called pairs unnecessarily')
	end
	function mt.__ipairs(t)
		-- Works just like the default ipairs, except respecting __index
		return function(t, i)
			local v = t[i + 1]
			if v ~= nil then
				return i + 1, v
			end
		end, t, 0
	end
	local args = getArgs(setmetatable({}, mt))
	for k,v in ipairs(args) do
		if v == 'bar' then
			break
		end
	end
end

--------------------------------------------------------------------------
-- Test argument translation
--------------------------------------------------------------------------

function suite:testTranslationIndex()
	local args = getArgs({F00 = 'one', ['8@r'] = 'two', ['8@z'] = 'three', qUx = 'four', foo = 'nope', untranslated = 'yep'}, {translate = d.translate})
	self:assertEquals('one', args.foo)
	self:assertEquals('two', args.bar)
	self:assertEquals('three', args.baz)
	self:assertEquals('four', args.qux)
	self:assertEquals('yep', args.untranslated)
end

function suite:testTranslationPairsWithAutoBacktranslate()
	local args = getArgs({F00 = 'one', ['8@r'] = 'two', ['8@z'] = 'three', qUx = 'four', foo = 'nope', untranslated = 'yep'}, {translate = d.translate})
	local cleanArgs = {}
	for k,v in pairs(args) do
		cleanArgs[k] = v
	end
	self:assertDeepEquals(
		{
			foo = 'one',
			bar = 'two',
			baz = 'three',
			qux = 'four',
			untranslated = 'yep'
		},
		cleanArgs
	)
end

function suite:testTranslationPairsWithBacktranslate()
	local args = getArgs({F00 = 'one', ['8@r'] = 'two', ['8@z'] = 'three', qUx = 'four', foo = 'nope', untranslated = 'yep'}, {translate = d.translate, backtranslate = {F00 = 'foo'}})
	local cleanArgs = {}
	for k,v in pairs(args) do
		cleanArgs[k] = v
	end
	self:assertDeepEquals(
		{
			foo = 'one',
			['8@r'] = 'two',
			['8@z'] = 'three',
			qUx = 'four',
			untranslated = 'yep'
		},
		cleanArgs
	)
end

function suite:testTranslationPairsWithoutBacktranslate()
	local args = getArgs({F00 = 'one', ['8@r'] = 'two', ['8@z'] = 'three', qUx = 'four', foo = 'nope', untranslated = 'yep'}, {translate = d.translate, backtranslate = false})
	local cleanArgs = {}
	for k,v in pairs(args) do
		cleanArgs[k] = v
	end
	self:assertDeepEquals(
		{
			F00 = 'one',
			['8@r'] = 'two',
			['8@z'] = 'three',
			qUx = 'four',
			foo = 'nope',
			untranslated = 'yep'
		},
		cleanArgs
	)
end

function suite:testTranslationNewindex()
	local args = getArgs({F00 = 'one', ['8@r'] = 'two', ['8@z'] = 'three', qUx = 'four', foo = 'nope', untranslated = 'yep'}, {translate = d.translate, backtranslate = false})
	args.foo = 'changed1'
	args.untranslated = 'changed2'
	local cleanArgs = {}
	for k,v in pairs(args) do
		cleanArgs[k] = v
	end
	self:assertDeepEquals(
		{
			F00 = 'changed1',
			['8@r'] = 'two',
			['8@z'] = 'three',
			qUx = 'four',
			foo = 'nope',
			untranslated = 'changed2'
		},
		cleanArgs
	)
end

function suite:test_argument()
	local currentFrame = mw.getCurrentFrame()
	currentFrame.args[5] = 555;
	local args = getArgs(currentFrame)
	self:assertEquals('nil', type(args.foo))
end

return suite