测试Lua脚本是否破译

Testing lua script with busted

我正在尝试测试我们的Freeswitch lua脚本是否崩溃,并且遇到了麻烦。要点是,我需要能够监视以下类似的代码

1
2
local req_host = session:getVariable('sip_req_host')
session:setVariable('curl_timeout', 0)

但是我似乎无法弄清楚如何构建应将_G.session设置为的对象。我可以找到的关于如何使用破获的最好/唯一的好例子是在https://github.com/chris-allnutt/unit-tested-corona/blob/master/mocks/button.lua,但它似乎使用了相同的方法用于构建被破坏的文档执行的模拟对象的简单语法。

1
2
3
4
5
local button = {
  x = 0,
  y = 0,
  addEventListener = function() end
}

我可以看到这对于不需要返回任何内容的简单函数将如何工作,但是我需要能够使用getVariable和setVariable函数在会话对象中获取和设置变量。我的简单模拟对象如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Session = {}
Session.__index = Session

function Session.create(params)
  local session = {}
  setmetatable(session, Session)
  session.params = params
  return session
end

function Session:getVariable(key)
  return self.params[key]
end

function Session:setVariable(key, val)
  self.params[key] = val
end

function Session:execute(cmd, code)
end

,测试如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
require"busted"
require("test_utils")

describe("Test voip lua script", function()
  it('Test webrtc bad domain', function()
    domain = 'rtc.baddomain.com';
    session_params = {['sip_req_host'] = domain,
                      ['sip_req_user'] = 'TEST-WebRTC-Client',
                      ["sip_from_user"] = 'testwebrtc_p_12345',
                      ['sip_call_id'] = 'test@call_id',
                      ['sip_authorized'] = 'false'}
    exec_str = 'sofia_contact TEST-WebRTC-Client@'..domain;
    api_params = {[exec_str] = 'error/user_not_registered'}

    _G.session = mock(Session.create(session_params), 'execute')
    _G.api = API.create(api_params)
    _G.freeswitch = Freeswitch.create()

    dofile("tested_script.lua")

    assert.spy(_G.session.execute).called_with("respond","407")
  end)
end)

我最终遇到以下异常。
/usr/local/share/lua/5.2/luassert/spy.lua:78:尝试为函数值建立索引

luassert(已破坏的库的依赖项)在以下if语句中抛出此异常

1
2
3
4
5
6
7
77:local function called_with(state, arguments)
78:  if rawget(state,"payload") and rawget(state,"payload").called_with then
79:    return state.payload:called_with(arguments)
80:  else
81:    error("'called_with' must be chained after 'spy(aspy)'")
82:  end
83:end

我对lua还是很陌生,所以似乎我只是缺少了该语言的一些明显部分,但是任何帮助或指针都将不胜感激。


因此,经过一天调试后,我发现的答案是,是的,您确实需要使用表作为调用模拟对象。但是,由于在构建具有可调用参数的对象时lua是一种非常宽容的语言,因此它仍然可以正常工作。由于与该问题无关的原因,我围绕该对象构建了package器,但是您可以在下面看到我最终所做的工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function SessionConstructor.create(params)
  local session_constructor = {}
  setmetatable(session_constructor, SessionConstructor)
  session_constructor.session = {}
  session_constructor.session.params = params
  session_constructor.session.getVariable = function(self,key)
    return self.params[key]
  end
  session_constructor.session.setVariable = function(self, key, val)
    self.params[key] = val
  end
  session_constructor.session.execute = function(self, cmd, code)
  end

  return session_constructor
end

function SessionConstructor:construct()
  return self.session
end

一个重要的警告,因为您必须将self传递到将使用lua的":"语法调用的函数,因此监视间谍函数的方法的确发生了变化,如下面的测试文件所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
require"busted"
require"test_utils"

describe("Test voip lua script", function()
  it('Test webrtc bad domain', function()
    domain = 'rtc.baddomain.com';
    session_params = {['sip_req_host'] = domain,
                      ['sip_req_user'] = 'TEST-WebRTC-Client',
                      ["sip_from_user"] = 'testwebrtc_p_12345',
                      ['sip_call_id'] = 'test@call_id',
                      ['sip_authorized'] = 'false'}
    local sess_con = SessionConstructor.create(session_params)

    exec_str = 'sofia_contact TEST-WebRTC-Client@'..domain;    
    local api_con = APIConstructor.create()
    api_con:expect_exec(exec_str, 'error/user_not_registered')

    _G.session = mock(sess_con:construct())
    _G.api = mock(api_con:construct())
    _G.freeswitch = create_freeswitch()

    dofile("tested_script.lua")

    assert.spy(session.execute).was.called_with(session,"respond","407")
    assert.spy(session.execute).was_not.called_with("respond","407") --This is unfortunate
  end)
end)

FreeSWITCH中的

mod_lua使用了稍微定制的Lua解释器,并且您似乎使用了安装在主机上的其他Lua解释器。我想他们不会轻易合作。


我对busted bin脚本进行了一些反向工程,并转到以下脚本(我们称其为runner.lua):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
busted = require 'busted.core'()
local environment = require 'busted.environment'(busted.context)

function unpack(t, i)
  i = i or 1
  if t[i] ~= nil then
    return t[i], unpack(t, i + 1)
  end
end
busted.getTrace = function(element, level, msg)
    level = level or  3

    local info = debug.getinfo(level, 'Sl')
    info.traceback = debug.traceback('', level)
    info.message = msg
    if msg ~= nil then
      freeswitch.consoleLog("NOTICE", msg)
    end

    local file = busted.getFile(element)
    return file.getTrace(file.name, info)
end

busted.safe = function(descriptor, run, element, setenv)
    if setenv and (type(run) == 'function' or getmetatable(run).__call) then
      -- prioritize __call if it exists, like in files
      environment.wrap(getmetatable(run).__call or run)
    end

    busted.context.push(element)
    local trace, message

    local ret = { xpcall(run, function(msg)
      message = busted.rewriteMessage(element, msg)
      freeswitch.consoleLog("ERR", message)
      trace = busted.getTrace(element, 3, msg)
    end) }

    if not ret[1] then
      busted.publish({ 'error', descriptor }, element, busted.context.parent(element), message, trace)
    end

    busted.context.pop()
    return unpack(ret)
end
require 'busted.init'(busted)

local checkTag = function(name, tag, modifier)
  local found = name:find('#' .. tag)        
  return (modifier == (found ~= nil))        
end                                          

local checkTags = function(name)              
  for i, tag in pairs(tags) do                
    if not checkTag(name, tag, true) then    
      return nil, false                      
    end                                      
  end                                        

  for i, tag in pairs(excludeTags) do        
    if not checkTag(name, tag, false) then    
      return nil, false                      
    end                                      
  end                                        

  return nil, true                            
end          

local getTrace =  function(filename, info)      
  local index = info.traceback:find('\
%s*%[C]'
)
  info.traceback = info.traceback:sub(1, index)
  return info, false                            
end                                            

local file = setmetatable({
  getTrace = getTrace
}, {
  __call = loadfile("/path/scripts/main_spec.lua")
})
busted.executors.file("main_spec.lua", file)

local failures = 0                      
local errors = 0                        

busted.subscribe({ 'error' }, function()
  errors = errors + 1                  
end)                                    

busted.subscribe({ 'test', 'end' }, function(element, parent, status)
  if status == 'failure' then                              
    failures = failures + 1                                
  end                                                      
end)                                                        

busted.publish({ 'suite', 'start' })
busted.execute()
busted.publish({ 'suite', 'end' })
freeswitch.consoleLog("NOTICE","Failures:" .. failures)
freeswitch.consoleLog("NOTICE","Errors:" .. errors)

该脚本仅对一个文件(/path/scripts/main_spec.lua)起作用,但仍然可用。
您可以通过Freeswitch控制台中的luarun来运行此runner.lua脚本:

1
2
fs_cli
luarun /path/to/runner.lua

您将在此处获得输出。