local singleton = require('mc.singleton')
local utils = require('mc.utils')
local m = {}
m.__index = m
function m.new()
return setmetatable({
pre_tree = {},
tree = {
interfaces = {},
children = {},
},
mocks = {},
invokes = {},
}, m)
end
local function key_contains(element, tab)
for k, _ in pairs(tab) do
if k == element then
return true
end
end
return false
end
local function split_path(path)
if string.sub(path, 1, 1) ~= '/' then
return nil
end
path = string.sub(path, 2)
return utils.split(path, '/')
end
local function table_contains_tables(t1, t2, ignore_case)
for k1, v1 in pairs(t1) do
for k2, v2 in pairs(t2) do
if ignore_case and type(v1) == 'string' and type(v2) == 'string' then
v1 = v1:lower()
v2 = v2:lower()
end
if k1 == k2 and v1 == v2 then
return true
end
end
end
return false
end
local function get_path_dfs(res, node, interface, filter, ignore_case, path)
for k, v in pairs(node.interfaces) do
if k == interface and table_contains_tables(v, filter, ignore_case) then
res[#res + 1] = path
end
end
for k, v in pairs(node.children) do
get_path_dfs(res, v, interface, filter, ignore_case, k)
end
end
local function is_valid_path_dfs(node, path)
for k, v in pairs(node.children) do
if k == path or is_valid_path_dfs(v, path) then
return true
end
end
return false
end
function m:get_object(path, interface)
local node = self:get_node(path)
if not node then
return nil
end
return node.interfaces[interface]
end
function m:change_prop(path, interface, prop, val)
local node = self:get_node(path)
if not node then
return nil
end
if node.interfaces[interface][prop] then
node.interfaces[interface][prop] = val
else
return
end
if node.interfaces[interface]['_' .. prop] then
node.interfaces[interface]['_' .. prop]()
end
end
function m:get_sub_paths(path, depth, interfaces)
local node = self:get_node(path)
if not node then
return nil
end
local father = { [path] = node }
while depth > 0 do
local son = {}
for _, v in pairs(father) do
for sub_path, sub_node in pairs(v.children) do
son[sub_path] = sub_node
end
end
father = son
depth = depth - 1
end
local sub_paths = {}
if interfaces[1] == nil then
for k, _ in pairs(father) do
sub_paths[#sub_paths + 1] = k
end
return sub_paths
end
for k, v in pairs(father) do
for i, _ in pairs(v.interfaces) do
if utils.array_contains(interfaces, i) then
sub_paths[#sub_paths + 1] = k
break
end
end
end
return sub_paths
end
function m:get_path(interface, filter, ignore_case)
local node = self.tree
local res = {}
get_path_dfs(res, node, interface, filter, ignore_case, '/')
return res
end
function m:is_valid_path(path)
local node = self.tree
return is_valid_path_dfs(node, path)
end
function m:update_interface(interface, properties, methods)
local t = interface == nil and {} or interface
if properties then
for k, v in pairs(properties) do
t[k] = v
end
end
if not methods then
return t
end
for k, v in pairs(methods) do
t[k] = function()
local dt_mdb = require('tree')
local obj = dt_mdb.get_instance()
obj:mock(v.mocks)
return v.rsp
end
end
return t
end
function m:get_node(path)
local tab = split_path(path)
if type(tab) ~= 'table' then
return nil
end
local node = self.tree
local p = ''
for _, v in pairs(tab) do
p = p .. '/' .. v
if node.children[p] == nil then
return
end
node = node.children[p]
end
return node
end
function m:find_node(path)
local tab = split_path(path)
if type(tab) ~= 'table' then
return nil
end
local node = self.tree
local p = ''
for _, v in pairs(tab) do
p = p .. '/' .. v
if node.children[p] == nil then
local child = {}
child.children = {}
child.interfaces = {}
node.children[p] = child
end
node = node.children[p]
end
return node
end
function m:update_node(path, interface, properties, methods)
local node = self:find_node(path)
if not node then
return
end
node.interfaces[interface] = self:update_interface(node.interfaces[interface], properties, methods)
end
function m:delete_node(path)
local tab = split_path(path)
if type(tab) ~= 'table' then
return nil
end
local node = self.tree
local p = ''
for _, v in pairs(tab) do
p = p .. '/' .. v
if node.children[p] == nil then
return
end
if p == path then
node.children[p] = nil
end
node = node.children[p]
end
end
function m:register_func(path, interface, properties)
local node = self:find_node(path)
if not node then
return
end
for k, v in pairs(properties) do
node.interfaces[interface]['_' .. k] = function()
self:mock(v)
end
end
end
function m:mock_one(mock)
local path = mock.path
local interface = mock.interface
local properties = mock.properties
local methods = mock.methods
local operation = mock.operation
if not path then
print('The mock lacks \'path\'.')
return
end
if operation == 'Delete' then
self:delete_node(path)
return
end
self:update_node(path, interface, properties, methods)
end
function m:invoke_one(invoke)
local path = invoke.path
local interface = invoke.interface
local properties = invoke.properties
if not path or not interface or not properties then
return
end
self:register_func(path, interface, properties)
end
function m:mock(mocks)
if type(mocks) ~= 'table' then
return
end
for _, v in pairs(mocks) do
if key_contains(v, self.mocks) then
self:mock_one(self.mocks[v])
end
end
end
function m:invoke(invokes)
if type(invokes) ~= 'table' then
return
end
for _, v in pairs(invokes) do
if key_contains(v, self.invokes) then
self:invoke_one(self.invokes[v])
end
end
end
function m:backup_stub()
self.pre_tree = utils.table_copy(self.tree)
end
function m:clear_stub()
self.tree = {
interfaces = {},
children = {},
}
end
function m:recover_stub()
self.tree = utils.table_copy(self.pre_tree)
end
function m:clear()
self:clear_stub()
self.mocks = {}
self.invokes = {}
end
return singleton(m)