diff options
Diffstat (limited to 'www/wiki/extensions/Scribunto/tests/phpunit/engines/LuaCommon/CommonTests.lua')
-rw-r--r-- | www/wiki/extensions/Scribunto/tests/phpunit/engines/LuaCommon/CommonTests.lua | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/www/wiki/extensions/Scribunto/tests/phpunit/engines/LuaCommon/CommonTests.lua b/www/wiki/extensions/Scribunto/tests/phpunit/engines/LuaCommon/CommonTests.lua new file mode 100644 index 00000000..fef18edf --- /dev/null +++ b/www/wiki/extensions/Scribunto/tests/phpunit/engines/LuaCommon/CommonTests.lua @@ -0,0 +1,387 @@ +local testframework = require 'Module:TestFramework' + +local test = {} + +function test.clone1() + local x = 1 + local y = mw.clone( x ) + return ( x == y ) +end + +function test.clone2() + local x = { 'a' } + local y = mw.clone( x ) + assert( x ~= y ) + return testframework.deepEquals( x, y ) +end + +function test.clone2b() + local x = { 'a' } + local y = mw.clone( x ) + assert( x ~= y ) + y[2] = 'b' + return testframework.deepEquals( x, y ) +end + +function test.clone3() + local mt = { __add = function() end } + local x = {} + setmetatable( x, mt ) + local y = mw.clone( x ) + assert( getmetatable( x ) ~= getmetatable( y ) ) + return testframework.deepEquals( getmetatable( x ), getmetatable( y ) ) +end + +function test.clone4() + local x = {} + x.x = x + local y = mw.clone( x ) + assert( x ~= y ) + return y == y.x +end + +function test.setfenv1() + setfenv( 0, {} ) +end + +function test.setfenv2() + setfenv( 1000, {} ) +end + +function test.setfenv3() + local function jailbreak() + setfenv( 2, {} ) + end + local new_setfenv, new_getfenv = mw.makeProtectedEnvFuncsForTest( { [_G] = true }, {} ) + setfenv( jailbreak, {setfenv = new_setfenv} ) + jailbreak() +end + +function test.setfenv4() + -- Set an unprotected environment at a higher stack level than a protected + -- environment. It's assumed that any higher-level environment will protect + -- itself with its own setfenv wrapper, so this succeeds. + local function level3() + local protected = {setfenv = setfenv, getfenv = getfenv, mw = mw} + local function level2() + local function level1() + setfenv( 3, {} ) + end + + local env = {} + env.setfenv, env.getfenv = mw.makeProtectedEnvFuncsForTest( + {[protected] = true}, {} ) + setfenv( level1, env )() + end + setfenv( level2, protected )() + end + local unprotected = {setfenv = setfenv, getfenv = getfenv, mw = mw} + setfenv( level3, unprotected )() + assert( getfenv( level3 ) ~= unprotected ) + return 'ok' +end + +function test.setfenv5() + local function allowed() + (function() setfenv( 2, {} ) end )() + end + local new_setfenv, new_getfenv = mw.makeProtectedEnvFuncsForTest( { [_G] = true }, {} ) + setfenv( allowed, {setfenv = new_setfenv} )() + return 'ok' +end + +function test.setfenv6() + local function target() end + local function jailbreak() + setfenv( target, {} ) + end + local new_setfenv, new_getfenv = mw.makeProtectedEnvFuncsForTest( {}, { [target] = true } ) + setfenv( jailbreak, {setfenv = new_setfenv} )() +end + +function test.setfenv7() + setfenv( {}, {} ) +end + +function test.getfenv1() + assert( getfenv( 1 ) == _G ) + return 'ok' +end + +function test.getfenv2() + getfenv( 0 ) +end + +function test.getfenv3() + local function foo() + return getfenv( 2 ) + end + + local function bar() + return foo() + end + + -- The "at level #" bit varies between environments, so + -- catch the error and strip that part out + local ok, err = pcall( bar ) + if not ok then + err = string.gsub( err, '^%S+:%d+: ', '' ) + err = string.gsub( err, ' at level %d$', '' ) + error( err ) + end +end + +function test.executeExpensiveCalls( n ) + for i = 1, n do + mw.incrementExpensiveFunctionCount() + end + return 'Did not error out' +end + +function test.stringMetatableHidden1() + return getmetatable( "" ) +end + +function test.stringMetatableHidden2() + string.foo = 42 + return ("").foo +end + +local pairs_test_table = {} +setmetatable( pairs_test_table, { + __pairs = function () return 1, 2, 3, 'ignore' end, + __ipairs = function () return 4, 5, 6, 'ignore' end, +} ) + +function test.noLeaksViaPackageLoaded() + assert( package.loaded.debug == debug, "package.loaded.debug ~= debug" ) + assert( package.loaded.string == string, "package.loaded.string ~= string" ) + assert( package.loaded.math == math, "package.loaded.math ~= math" ) + assert( package.loaded.io == io, "package.loaded.io ~= io" ) + assert( package.loaded.os == os, "package.loaded.os ~= os" ) + assert( package.loaded.table == table, "package.loaded.table ~= table" ) + assert( package.loaded._G == _G , "package.loaded._G ~= _G " ) + assert( package.loaded.coroutine == coroutine, "package.loaded.coroutine ~= coroutine" ) + assert( package.loaded.package == package, "package.loaded.package ~= package" ) + return 'ok' +end + +test.loadData = {} + +function test.loadData.get( ... ) + local d = mw.loadData( 'Module:CommonTests-data' ) + for i = 1, select( '#', ... ) do + local k = select( i, ... ) + d = d[k] + end + return d +end + +function test.loadData.set( v, ... ) + local d = mw.loadData( 'Module:CommonTests-data' ) + local n = select( '#', ... ) + for i = 1, n - 1 do + local k = select( i, ... ) + d = d[k] + end + d[select( n, ... )] = v + return d[select( n, ... )] +end + +function test.loadData.recursion() + local d = mw.loadData( 'Module:CommonTests-data' ) + return d == d.t, d.t == d.t.t, d.table2 == d.table +end + +function test.loadData.iterate( func ) + local d = mw.loadData( 'Module:CommonTests-data' ) + local ret = {} + for k, v in func( d.table ) do + ret[k] = v + end + return ret +end + +function test.loadData.setmetatable() + local d = mw.loadData( 'Module:CommonTests-data' ) + setmetatable( d, {} ) + return 'setmetatable succeeded' +end + +function test.loadData.rawset() + -- We can't easily prevent rawset (and it's not worth trying to redefine + -- it), but we can make sure it doesn't affect other instances of the data + local d1 = mw.loadData( 'Module:CommonTests-data' ) + local d2 = mw.loadData( 'Module:CommonTests-data' ) + rawset( d1, 'str', 'ugh' ) + local d3 = mw.loadData( 'Module:CommonTests-data' ) + return d1.str, d2.str, d3.str +end + +return testframework.getTestProvider( { + { name = 'clone', func = test.clone1, + expect = { true }, + }, + { name = 'clone table', func = test.clone2, + expect = { true }, + }, + { name = 'clone table then modify', func = test.clone2b, + expect = { false, { 2 }, nil, 'b' }, + }, + { name = 'clone table with metatable', func = test.clone3, + expect = { true }, + }, + { name = 'clone recursive table', func = test.clone4, + expect = { true }, + }, + + { name = 'setfenv global', func = test.setfenv1, + expect = "'setfenv' cannot set the global environment, it is protected", + }, + { name = 'setfenv invalid level', func = test.setfenv2, + expect = "'setfenv' cannot set an environment at a level greater than 10", + }, + { name = 'setfenv invalid environment', func = test.setfenv3, + expect = "'setfenv' cannot set the requested environment, it is protected", + }, + { name = 'setfenv on unprotected past protected', func = test.setfenv4, + expect = { 'ok' }, + }, + { name = 'setfenv from inside protected', func = test.setfenv5, + expect = { 'ok' }, + }, + { name = 'setfenv protected function', func = test.setfenv6, + expect = "'setfenv' cannot be called on a protected function", + }, + { name = 'setfenv on a non-function', func = test.setfenv7, + expect = "'setfenv' can only be called with a function or integer as the first argument", + }, + + { name = 'getfenv(1)', func = test.getfenv1, + expect = { 'ok' }, + }, + { name = 'getfenv(0)', func = test.getfenv2, + expect = "'getfenv' cannot get the global environment", + }, + { name = 'getfenv with tail call', func = test.getfenv3, + expect = "no function environment for tail call", + }, + + { name = 'Not quite too many expensive function calls', + func = test.executeExpensiveCalls, args = { 10 }, + expect = { 'Did not error out' } + }, + + { name = 'Too many expensive function calls', + func = test.executeExpensiveCalls, args = { 11 }, + expect = 'too many expensive function calls' + }, + + { name = 'string metatable is hidden', func = test.stringMetatableHidden1, + expect = { nil } + }, + + { name = 'string is not string metatable', func = test.stringMetatableHidden2, + expect = { nil } + }, + + { name = 'pairs with __pairs', + func = pairs, args = { pairs_test_table }, + expect = { 1, 2, 3 }, + }, + + { name = 'ipairs with __ipairs', + func = ipairs, args = { pairs_test_table }, + expect = { 4, 5, 6 }, + }, + + { name = 'package.loaded does not leak references to out-of-environment objects', + func = test.noLeaksViaPackageLoaded, + expect = { 'ok' }, + }, + + { name = 'mw.loadData, returning non-table', + func = mw.loadData, args = { 'Module:CommonTests-data-fail1' }, + expect = "Module:CommonTests-data-fail1 returned string, table expected", + }, + { name = 'mw.loadData, containing function', + func = mw.loadData, args = { 'Module:CommonTests-data-fail2' }, + expect = "data for mw.loadData contains unsupported data type 'function'", + }, + { name = 'mw.loadData, containing table-with-metatable', + func = mw.loadData, args = { 'Module:CommonTests-data-fail3' }, + expect = "data for mw.loadData contains a table with a metatable", + }, + { name = 'mw.loadData, containing function as key', + func = mw.loadData, args = { 'Module:CommonTests-data-fail4' }, + expect = "data for mw.loadData contains unsupported data type 'function'", + }, + { name = 'mw.loadData, containing table-with-metatable as key', + func = mw.loadData, args = { 'Module:CommonTests-data-fail5' }, + expect = "data for mw.loadData contains a table as a key", + }, + { name = 'mw.loadData, getter (true)', + func = test.loadData.get, args = { 'true' }, + expect = { true } + }, + { name = 'mw.loadData, getter (false)', + func = test.loadData.get, args = { 'false' }, + expect = { false } + }, + { name = 'mw.loadData, getter (NaN)', + func = test.loadData.get, args = { 'NaN' }, + expect = { 0/0 } + }, + { name = 'mw.loadData, getter (inf)', + func = test.loadData.get, args = { 'inf' }, + expect = { 1/0 } + }, + { name = 'mw.loadData, getter (num)', + func = test.loadData.get, args = { 'num' }, + expect = { 12.5 } + }, + { name = 'mw.loadData, getter (str)', + func = test.loadData.get, args = { 'str' }, + expect = { 'foo bar' } + }, + { name = 'mw.loadData, getter (table.2)', + func = test.loadData.get, args = { 'table', 2 }, + expect = { 'two' } + }, + { name = 'mw.loadData, getter (t.t.t.t.str)', + func = test.loadData.get, args = { 't', 't', 't', 't', 'str' }, + expect = { 'foo bar' } + }, + { name = 'mw.loadData, getter recursion', + func = test.loadData.recursion, + expect = { true, true, true }, + }, + { name = 'mw.loadData, pairs', + func = test.loadData.iterate, args = { pairs }, + expect = { { 'one', 'two', 'three', foo = 'bar' } }, + }, + { name = 'mw.loadData, ipairs', + func = test.loadData.iterate, args = { ipairs }, + expect = { { 'one', 'two', 'three' } }, + }, + { name = 'mw.loadData, setmetatable', + func = test.loadData.setmetatable, + expect = "cannot change a protected metatable" + }, + { name = 'mw.loadData, setter (1)', + func = test.loadData.set, args = { 'ugh', 'str' }, + expect = "table from mw.loadData is read-only", + }, + { name = 'mw.loadData, setter (2)', + func = test.loadData.set, args = { 'ugh', 'table', 2 }, + expect = "table from mw.loadData is read-only", + }, + { name = 'mw.loadData, setter (3)', + func = test.loadData.set, args = { 'ugh', 't' }, + expect = "table from mw.loadData is read-only", + }, + { name = 'mw.loadData, rawset', + func = test.loadData.rawset, + expect = { 'ugh', 'foo bar', 'foo bar' }, + }, +} ) |