You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lua-lemock/build/unit/controller.lua

861 lines
20 KiB

-- ../src/unittestfiles.nw:46
-- ../src/misc.nw:7
------ THIS FILE IS TANGLED FROM LITERATE SOURCE FILES ------
-- Copyright (C) 2009 Tommy Pettersson <ptp@lysator.liu.se>
-- See terms in file COPYRIGHT, or at http://lemock.luaforge.net
-- ../src/unittestfiles.nw:47
require 'lunit'
module( 'unit.controller', lunit.testcase, package.seeall )
local class, object, qtostring, sfmt, add_to_set, elements_of_set
-- ../src/helperfunctions.nw:12
function object (class)
return setmetatable( {}, class )
end
function class (parent)
local c = object(parent)
c.__index = c
return c
end
-- ../src/unittestfiles.nw:53
-- ../src/tostring.nw:23
sfmt = string.format
function qtostring (v)
if type(v) == 'string' then
return sfmt( '%q', v )
else
return tostring( v )
end
end
-- ../src/unittestfiles.nw:54
-- ../src/helperfunctions.nw:47
function add_to_set (o, setname, element)
if not o[setname] then
o[setname] = {}
end
local l = o[setname]
for i = 1, #l do
if l[i] == element then return end
end
l[#l+1] = element
end
function elements_of_set (o, setname)
local l = o[setname]
local i = l and #l+1 or 0
return function ()
i = i - 1
if i > 0 then return l[i] end
end
end
-- ../src/unittestfiles.nw:55
-- ../src/main.nw:373
local mock_controller_map = setmetatable( {}, {__mode='k'} )
-- ../src/unittestfiles.nw:57
local Controller, Action
-- ../src/class/controller.nw:6
Controller = class()
-- Exported methods
-- ../src/restrictions.nw:595
function Controller:close (...)
if not self.is_recording then
error( "Can not insert close in replay mode.", 2 )
end
local action = self:get_last_action()
for _, close in ipairs{ ... } do
action:add_close( close )
end
return self -- for chaining
end
-- ../src/class/controller.nw:10
-- ../src/restrictions.nw:410
function Controller:depend (...)
if not self.is_recording then
error( "Can not add dependency in replay mode.", 2 )
end
local action = self:get_last_action()
for _, dependency in ipairs{ ... } do
action:add_depend( dependency )
end
return self -- for chaining
end
-- ../src/class/controller.nw:11
-- ../src/main.nw:617
function Controller:error (value)
if not self.is_recording then
error( "Error called during replay.", 2 )
end
local action = self:get_last_action()
if action.has_returnvalue or action.throws_error then
error( "Returns and/or Error called twice for same action.", 2 )
end
action.throws_error = true
action.errorvalue = value
return self -- for chaining
end
-- ../src/class/controller.nw:12
-- ../src/restrictions.nw:158
function Controller:label (...)
if not self.is_recording then
error( "Can not add labels in replay mode.", 2 )
end
local action = self:get_last_action()
for _, label in ipairs{ ... } do
action:add_label( label )
end
return self -- for chaining
end
-- ../src/class/controller.nw:13
-- ../src/main.nw:462
function Controller:mock ()
if not self.is_recording then
error( "New mock during replay.", 2 )
end
local m = object( Mock.record )
mock_controller_map[m] = self
return m
end
-- ../src/class/controller.nw:14
-- ../src/main.nw:421
function Controller:new ()
local mc = object( self )
mc.actionlist = {}
mc.is_recording = true
return mc
end
-- ../src/class/controller.nw:15
-- ../src/main.nw:671
function Controller:replay ()
if not self.is_recording then
error( "Replay called twice.", 2 )
end
self.is_recording = false
for m, mc in pairs( mock_controller_map ) do
if mc == self then
setmetatable( m, Mock.replay )
end
end
self:update_dependencies()
self:assert_no_dependency_cycles()
end
-- ../src/class/controller.nw:16
-- ../src/main.nw:571
function Controller:returns (...)
if not self.is_recording then
error( "Returns called during replay.", 2 )
end
local action = self:get_last_action()
assert( not action.is_callable, "lemock internal error" )
if not action.can_return then
error( "Previous action can not return anything.", 2 )
end
if action.has_returnvalue or action.throws_error then
error( "Returns and/or Error called twice for same action.", 2 )
end
action:set_returnvalue(...)
return self -- for chaining
end
-- ../src/class/controller.nw:17
-- ../src/restrictions.nw:74
function Controller:times (min, max)
if not self.is_recording then
error( "Can not set times in replay mode.", 0 )
end
self:get_last_action():set_times( min, max )
return self -- for chaining
end
-- convenience functions
function Controller:anytimes() return self:times( 0, math.huge ) end
function Controller:atleastonce() return self:times( 1, math.huge ) end
-- ../src/class/controller.nw:18
-- ../src/main.nw:754
function Controller:verify ()
if self.is_recording then
error( "Verify called during record.", 2 )
end
for a in self:actions() do
a:assert_satisfied()
end
end
-- ../src/class/controller.nw:19
-- Protected methods
-- ../src/main.nw:145
function Controller:actions (q)
local l = self.actionlist
local i = 0
return function ()
i = i + 1
return l[i]
end
end
-- ../src/class/controller.nw:22
-- ../src/main.nw:56
function Controller:add_action (a)
assert( a ~= nil, "lemock internal error" ) -- breaks array property
table.insert( self.actionlist, a )
end
-- ../src/class/controller.nw:23
-- ../src/restrictions.nw:489
function Controller:assert_no_dependency_cycles ()
local function is_in_path (label, path)
if not path then return false end -- is root
for _, l in ipairs( path ) do
if l == label then return true end
end
if path.prev then return is_in_path( label, path.prev ) end
return false
end
local function can_block (action, node)
for _, label in ipairs( node ) do
if action:has_label( label ) then return true end
end
return false
end
local function step (action, path)
local new_head
for label in action:depends() do
if is_in_path( label, path ) then
error( "Detected dependency cycle", 0 )
end
-- only create table if needed to reduce garbage
if not new_head then new_head = { prev=path } end
new_head[#new_head+1] = label
end
return new_head
end
local function search_depth_first (path)
for action in self:actions() do
if can_block( action, path ) then
local new_head = step( action, path )
if new_head then
search_depth_first( new_head )
end
end
end
end
for action in self:actions() do
local root = step( action, nil )
if root then search_depth_first( root ) end
end
end
-- ../src/class/controller.nw:24
-- ../src/restrictions.nw:616
function Controller:close_actions( ... ) -- takes iterator
for label in ... do
for candidate in self:actions() do
if candidate:has_label( label ) then
if not candidate:is_satisfied() then
error( "Closes unsatisfied action: "..candidate:tostring(), 0 )
end
candidate.is_closed = true
end
end
end
end
-- ../src/class/controller.nw:25
-- ../src/main.nw:177
function Controller:get_last_action ()
local l = self.actionlist
if #l == 0 then
error( "No action is recorded yet.", 0 )
end
return l[#l]
end
-- ../src/class/controller.nw:26
-- ../src/main.nw:88
function Controller:lookup (actual)
for action in self:actions() do
if action:match( actual ) then
return action
end
end
-- ../src/main.nw:111
local expected = {}
for _, a in ipairs( self.actionlist ) do
if a:is_expected() and not a.is_callable then
expected[#expected+1] = a:tostring()
end
end
table.sort( expected )
if #expected == 0 then
expected[1] = "(Nothing)"
end
-- ../src/main.nw:95
error( sfmt( "Unexpected action %s, expected:\n%s\n"
, actual:tostring()
, table.concat(expected,'\n')
)
, 0
)
end
-- ../src/class/controller.nw:27
-- ../src/main.nw:531
function Controller:make_callable (action)
if action.has_returnvalue then
error( "Can not call "..action.key..". It has a returnvalue.", 0 )
end
action.is_callable = true
action.min_replays = 0
action.max_replays = math.huge
end
-- ../src/class/controller.nw:28
-- ../src/main.nw:421
function Controller:new ()
local mc = object( self )
mc.actionlist = {}
mc.is_recording = true
return mc
end
-- ../src/class/controller.nw:29
-- ../src/main.nw:297
function Controller:replay_action ( action )
assert( action:is_expected(), "lemock internal error" )
assert( action.replay_count < action.max_replays, "lemock internal error" )
local was_satisfied = action:is_satisfied()
action.replay_count = action.replay_count + 1
if not was_satisfied and action.labellist and action:is_satisfied() then
self:update_dependencies()
end
if action.closelist then
self:close_actions( action:closes() )
end
end
-- ../src/class/controller.nw:30
-- ../src/restrictions.nw:457
function Controller:update_dependencies ()
local blocked = {}
for action in self:actions() do
for label in action:blocks() do
blocked[label] = true
end
end
local function is_blocked (action)
for label in action:depends() do
if blocked[label] then return true end
end
return false
end
for action in self:actions() do
action.is_blocked = is_blocked( action )
end
end
-- ../src/unittestfiles.nw:60
-- ../src/class/action.nw:24
Action = {}
-- abstract
-- ../src/class/action.nw:41
Action.generic = class()
-- ../src/restrictions.nw:607
function Action.generic:add_close (label)
add_to_set( self, 'closelist', label )
end
-- ../src/class/action.nw:44
-- ../src/restrictions.nw:443
function Action.generic:add_depend (d)
add_to_set( self, 'dependlist', d )
end
-- ../src/class/action.nw:45
-- ../src/restrictions.nw:207
function Action.generic:add_label (label)
add_to_set( self, 'labellist', label )
end
-- ../src/class/action.nw:46
-- ../src/main.nw:338
function Action.generic:assert_satisfied ()
assert( self.replay_count <= self.max_replays, "lemock internal error" )
if not (
-- ../src/main.nw:330
self.min_replays <= self.replay_count
-- ../src/main.nw:340
) then
error( sfmt( "Wrong replay count %d (expected %d..%d) for %s"
, self.replay_count
, self.min_replays, self.max_replays
, self:tostring()
)
, 0
)
end
end
-- ../src/class/action.nw:47
-- ../src/restrictions.nw:220
function Action.generic:blocks ()
if self:is_satisfied() then
return function () end
end
return elements_of_set( self, 'labellist' )
end
-- ../src/class/action.nw:48
-- ../src/restrictions.nw:630
function Action.generic:closes ()
return elements_of_set( self, 'closelist' )
end
-- ../src/class/action.nw:49
-- ../src/restrictions.nw:448
function Action.generic:depends ()
return elements_of_set( self, 'dependlist' )
end
-- ../src/class/action.nw:50
-- ../src/restrictions.nw:212
function Action.generic:has_label (l)
for x in elements_of_set( self, 'labellist' ) do
if x == l then return true end
end
return false
end
-- ../src/class/action.nw:51
-- ../src/main.nw:247
function Action.generic:is_expected ()
return self.replay_count < self.max_replays
and not self.is_blocked
and not self.is_closed
end
-- ../src/class/action.nw:52
-- ../src/main.nw:333
function Action.generic:is_satisfied ()
return
-- ../src/main.nw:330
self.min_replays <= self.replay_count
-- ../src/main.nw:335
end
-- ../src/class/action.nw:53
-- ../src/main.nw:269
function Action.generic:match (key)
if getmetatable(self) ~= getmetatable(key) then return false end
if self.mock ~= key.mock then return false end
return self:is_expected()
end
-- ../src/class/action.nw:54
-- ../src/main.nw:219
function Action.generic:new (mock)
local a = object( self )
a.mock = mock
a.replay_count = 0
a.min_replays = 1
a.max_replays = 1
return a
end
-- ../src/class/action.nw:55
-- ../src/restrictions.nw:102
function Action.generic:set_times (a, b)
min = a or 1
max = b or min
min, max = tonumber(min), tonumber(max)
if (not min) or (not max) or (min >= math.huge)
or (min ~= min) or (max ~= max) -- NaN
or (min < 0) or (max <= 0) or (min > max) then
error( sfmt( "Unrealistic time arguments (%s, %s)"
, qtostring( min )
, qtostring( max )
)
, 0
)
end
self.min_replays = min
self.max_replays = max
end
-- ../src/class/action.nw:28
-- ../src/class/action.nw:59
Action.generic_call = class( Action.generic )
Action.generic_call.can_return = true
-- ../src/action/generic_call.nw:76
function Action.generic_call:get_returnvalue ()
if self.has_returnvalue then
return self.returnvalue:unpack()
end
end
-- ../src/class/action.nw:63
-- ../src/action/generic_call.nw:56
function Action.generic_call:set_returnvalue (...)
self.returnvalue = Argv:new(...)
self.has_returnvalue = true
end
-- ../src/class/action.nw:64
-- ../src/action/generic_call.nw:45
function Action.generic_call:match (q)
if not Action.generic.match( self, q ) then return false end
if not self.argv:equal( q.argv ) then return false end
return true
end
-- ../src/class/action.nw:66
-- ../src/action/generic_call.nw:32
function Action.generic_call:new (m, ...)
local a = Action.generic.new( self, m )
a.argv = Argv:new(...)
return a
end
-- ../src/class/action.nw:29
-- concrete
-- ../src/class/action.nw:93
Action.call = class( Action.generic_call )
-- ../src/action/call.nw:118
function Action.call:match (q)
if not Action.generic_call.match( self, q ) then return false end
if self.key ~= q.key then return false end
return true
end
-- ../src/class/action.nw:96
-- ../src/action/call.nw:82
function Action.call:new (m, key, ...)
local a = Action.generic_call.new( self, m, ... )
a.key = key
return a
end
-- ../src/class/action.nw:97
-- ../src/tostring.nw:101
function Action.call:tostring ()
if self.has_returnvalue then
return sfmt( "call %s(%s) => %s"
, tostring(self.key)
, self.argv:tostring()
, self.returnvalue:tostring()
)
else
return sfmt( "call %s(%s)"
, tostring(self.key)
, self.argv:tostring()
)
end
end
-- ../src/class/action.nw:32
-- ../src/class/action.nw:81
Action.index = class( Action.generic )
Action.index.can_return = true
-- ../src/action/index.nw:134
function Action.index:get_returnvalue ()
return self.returnvalue
end
-- ../src/class/action.nw:85
-- ../src/action/index.nw:85
function Action.index:set_returnvalue (v)
self.returnvalue = v
self.has_returnvalue = true
end
-- ../src/class/action.nw:86
-- ../src/action/index.nw:123
function Action.index:match (q)
if not Action.generic.match( self, q ) then return false end
if self.key ~= q.key then return false end
return true
end
-- ../src/class/action.nw:88
-- ../src/action/index.nw:67
function Action.index:new (m, key)
local a = Action.generic.new( self, m )
a.key = key
return a
end
-- ../src/class/action.nw:89
-- ../src/tostring.nw:70
function Action.index:tostring ()
local key = 'index '..tostring( self.key )
if self.has_returnvalue then
return sfmt( "index %s => %s"
, tostring( self.key )
, qtostring( self.returnvalue )
)
elseif self.is_callable then
return sfmt( "index %s()"
, tostring( self.key )
)
else
return sfmt( "index %s"
, tostring( self.key )
)
end
end
-- ../src/class/action.nw:33
-- ../src/class/action.nw:73
Action.newindex = class( Action.generic )
-- ../src/action/newindex.nw:102
function Action.newindex:match (q)
if not Action.generic.match( self, q ) then return false end
if self.key ~= q.key then return false end
if not value_equal( self.val, q.val )
and self.val ~= Argv.ANYARG
and q.val ~= Argv.ANYARG then return false end
return true
end
-- ../src/class/action.nw:76
-- ../src/action/newindex.nw:54
function Action.newindex:new (m, key, val)
local a = Action.generic.new( self, m )
a.key = key
a.val = val
return a
end
-- ../src/class/action.nw:77
-- ../src/tostring.nw:45
function Action.newindex:tostring ()
return sfmt( "newindex %s = %s"
, tostring(self.key)
, qtostring(self.val)
)
end
-- ../src/class/action.nw:34
-- ../src/class/action.nw:101
Action.selfcall = class( Action.generic_call )
-- ../src/action/selfcall.nw:93
function Action.selfcall:match (q)
return Action.generic_call.match( self, q )
end
-- ../src/class/action.nw:104
-- ../src/action/selfcall.nw:61
function Action.selfcall:new (m, ...)
local a = Action.generic_call.new( self, m, ... )
return a
end
-- ../src/class/action.nw:105
-- ../src/tostring.nw:129
function Action.selfcall:tostring ()
if self.has_returnvalue then
return sfmt( "selfcall (%s) => %s"
, self.argv:tostring()
, self.returnvalue:tostring()
)
else
return sfmt( "selfcall (%s)"
, self.argv:tostring()
)
end
end
-- ../src/unittestfiles.nw:61
local A = Action.generic
Action = nil -- only allow generic action
function A:tostring () return '<dummy>' end
local mc
function setup ()
mc = Controller:new()
end
-- ../src/main.nw:125
function actions_dont_iterate_empty_list_test ()
for a in mc:actions() do
fail( "iterates on empty list" )
end
end
function actions_iterate_over_entire_list_exactly_once_test ()
local l = { {},{},{} }
for _, a in ipairs( l ) do
mc:add_action( a )
end
for a in mc:actions() do
assert_nil( a.check )
a.check = true
end
for _, a in ipairs( l ) do
assert_true( a.check )
end
end
-- ../src/unittestfiles.nw:73
-- ../src/main.nw:48
function add_action_at_the_end_test ()
mc:add_action( 7 )
mc:add_action( mc )
assert_equal( 7, mc.actionlist[1] )
assert_equal( mc, mc.actionlist[2] )
end
-- ../src/unittestfiles.nw:74
-- ../src/main.nw:162
function get_last_action_returns_last_element_test ()
local l = { 'a', 'foo', 17 }
for i = 1, #l do
mc:add_action( l[i] )
local res = mc:get_last_action()
assert_equal( l[i], res )
end
end
function get_last_action_fails_on_empty_list_test ()
local ok, err = pcall( function() mc:get_last_action() end )
assert_false( ok, "Found last action in empty list" )
assert_match( "No action is recorded yet", err )
end
-- ../src/unittestfiles.nw:75
-- ../src/restrictions.nw:143
function label_test ()
mc:add_action( A:new() )
mc:label( 'a', 'b' ):label( 'c', 'b' )
local a = mc:get_last_action()
local seen = {}
for l in a:blocks() do
seen[l] = true
end
assert_true( seen['a'] )
assert_true( seen['b'] )
assert_true( seen['c'] )
assert_nil( seen['d'] )
end
-- ../src/unittestfiles.nw:76
-- ../src/main.nw:71
function lookup_returns_first_matching_action_test ()
local Fake_action
-- ../src/misc.nw:12
Fake_action = class()
function Fake_action:new (x)
local a = object(Fake_action)
a.x = x
return a
end
function Fake_action:match (q)
return self.x < q.x
end
function Fake_action:is_expected ()
return true
end
function Fake_action:tostring ()
return '<faked action>'
end
function Fake_action:blocks ()
return function () end
end
Fake_action.depends = Fake_action.blocks
-- ../src/main.nw:74
local a1 = Fake_action:new(1)
local a2 = Fake_action:new(2)
local a3 = Fake_action:new(1)
local ok, err = pcall( function() mc:lookup( a1 ) end )
assert_false( ok, "match in empty list" )
assert_match( "Unexpected action <faked action>", err )
mc:add_action( a1 ) mc:add_action( a2 ) mc:add_action( a3 )
local ok, err = pcall( function() mc:lookup( a1 ) end )
assert_false( ok, "should not match any action" )
assert_match( "Unexpected action <faked action>", err )
assert_equal( a1, mc:lookup( a2 ), "did not find first match" )
end
-- ../src/unittestfiles.nw:77
-- ../src/main.nw:664
function replay_test ()
assert_true( mc.is_recording )
mc:replay()
assert_false( mc.is_recording )
end
-- ../src/unittestfiles.nw:78
-- ../src/main.nw:285
function replay_action_test ()
local a = A:new()
mc:add_action( a )
assert_true( a:is_expected() )
assert_false( a:is_satisfied() )
mc:replay_action( a )
assert_false( a:is_expected() )
assert_true( a:is_satisfied() )
assert_equal( 1, a.replay_count )
end