-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local lu = require 'luaunit'
local c_topo_monitor = require 'biz_topo.topo_monitor'
local c_business_connector = require 'biz_topo.class.business_connector'
local c_unit_configuration = require 'biz_topo.class.unit_configuration'
local signal = require 'mc.signal'
local bs = require 'mc.bitstring'
local skynet = require 'skynet'
local c_topo_reader = require 'biz_topo.topo_reader'
local ctx = require 'mc.context'
local sd_bus = require 'sd_bus'
local cmn = require 'common'
local log = require 'mc.logging'

TestTopoMonitor = {}
TestTopoMonitor.mock_complete_signal = signal.new()

local table_cache = require 'mc.table_cache'
local tbl_cache = table_cache.new()

local mock_complete_signal = signal.new()
local bus = false

function TestTopoMonitor:test_init_biz_connector_match_info()
    local mds_obj = {
        ['bmc.kepler.Systems.UnitConfigError'] = {
            Port1LinkInfo = 'xxx',
            Port2LinkInfo = 'xxx',
            Port1Status = 1,
            Port2Status = 1
        },
        Ports = {{Name = 'A3a', ID = 9, Offset = 0, Width = 8}, {Name = 'A3c', ID = 11, Offset = 8, Width = 8}}
    }
    local object = c_business_connector.new(mds_obj, 1, self.mock_complete_signal)
    object.ports[1].target_unit_uid = '0000000101'
    object.ports[1].target_unit_index = 1
    object.ports[1].target_unit_port_id = 1
    object.ports[1].status = 1
    object.ports[1].is_discovered = true
    object.ports[2].target_unit_uid = '0000000101'
    object.ports[2].target_unit_index = 1
    object.ports[2].target_unit_port_id = 1
    object.ports[2].status = 1
    object.ports[2].is_discovered = true

    c_topo_monitor.init_biz_connector_match_info(object)

    lu.assertEquals(object.ports[1].target_unit_uid, '')
    lu.assertEquals(object.ports[1].target_unit_index, 0)
    lu.assertEquals(object.ports[1].target_unit_port_id, 0)
    lu.assertEquals(object.ports[1].status, 0)
    lu.assertEquals(object.ports[1].is_discovered, false)
    lu.assertEquals(object.ports[2].target_unit_uid, '')
end

function TestTopoMonitor:test_init_config_match_info()
    local mds_obj = {
        ['bmc.kepler.Systems.UnitConfigError'] = {Port1LinkInfo = 'xxx'},
        Configurations = {
            {
                UID = '00000001040302023940',
                Index = 1,
                SrcPortName = {'A3a', 'A3c'},
                TargetPortID = {49, 50},
                Slot = {2, 3},
                Device = {0, 1}
            }
        }
    }
    local object = c_unit_configuration.new(mds_obj, 1)
    object.configs[1].matched_times = 1
    c_topo_monitor.init_config_match_info(object)
    lu.assertEquals(object.configs[1].matched_times, 0)
end

local bs_topo = bs.new([[<<
    target_port_id:8,
    index:8,
    uid:24/string,
    port_id:8,
    _:1/string,
    bcu_index:8,
    _:3/string
>>]])

local function create_biz_topo_node(test_data, slot)
    local data = ''
    for _, d in pairs(test_data) do
        data = data .. bs_topo:pack(d)
    end
    return {
        mds_obj = {
            Slot = slot,
            RefSmcChip = {
                Read = function(...)
                    return data
                end
            }
        }
    }
end

local function create_biz_conn_objs(test_data, bcu_index)
    local result = {}
    for _, d in pairs(test_data) do
        result[#result + 1] = c_business_connector.new({
            ['bmc.kepler.Systems.UnitConfigError'] = {Port1LinkInfo = '', Port1Status = 0,
            Port2Status = 0, Port2LinkInfo = ''},
            Ports = {d},
            BCUIndex = bcu_index
        }, 1, mock_complete_signal)
    end
    return result
end

local function create_unit_conf_objs(test_data)
    local result = {}
    for _, d in pairs(test_data) do
        result[#result + 1] = c_unit_configuration.new({
            ['bmc.kepler.Systems.UnitConfigError'] = {
                Port1LinkInfo = '', Port2LinkInfo = '',
                Port1Status = 0, Port2Status = 0},
            SlotType = d.slot_type,
            SlotNumber = d.slot_number,
            SlotSilkText = d.silk_text,
            Configurations = d.configs
        }, 1)
    end
    return result
end

-- TOPO检测SMC命令响应体
local bs_topo_resp = bs.new([[<<
    target_port_id:8,
    index:8,
    uid:24/string,
    port_id:8,
    _:1/string,
    bcu_index:8,
    _:3/string
>>]])

local ZONE_A <const> = 'A'
local ZONE_B <const> = 'B'
local ZONE_C <const> = 'C'
local ZONE_D <const> = 'D'
local ZONE_E <const> = 'E'
local ZONE_F <const> = 'F'

-- TOPO检测SMC命令字
local TOPO_CMD_OFFSET <const> = {
    [ZONE_A] = 0xc005100,
    [ZONE_B] = 0xc005500,
    [ZONE_C] = 0xc005900,
    [ZONE_D] = 0xc005d00,
    [ZONE_E] = 0xc006100,
    [ZONE_F] = 0xc006500
}

local TOPO_RESP_LEN <const> = 32           -- 单条响应数据大小为32Byte
local RESP_NUM_PER_ZONE <const> = 32       -- 完整的响应数量为32
local TOPO_RESP_LEN_PER_ZONE <const> = 1024 -- 完整的为32*32 Byte
local RETRY_TIMES <const> = 3              -- SMC数据读取异常重试次数
local DEFAULT_MASK<const> = 0xffffffff
local BLOCK_ACCESS_TYPE<const> = 1

local function topo_read(chip, offset, len)
    if not chip then
        return false
    end
    local ok, ret1, ret2
    for _ = 1, RETRY_TIMES do
        ok, ret1 = pcall(function()
            return chip:Read(ctx.new(), offset, len)
        end)
        if not ok then
            goto next
        end
        ok, ret2 = pcall(function()
            return chip:Read(ctx.new(), offset, len)
        end)
        if not ok then
            goto next
        end
        -- 比较两次读取结果，若不同则重新读取
        if ret1 == ret2 then
            break
        else
            ok = false
        end
        ::next::
    end

    return ok, ret1
end

local function is_resp_valid(resp)
    return resp.target_port_id ~= 0 and resp.target_port_id ~= 0xff
end

local function plugin_read_topo_info(chip, zone_support)
    local topo_info = {}
    local ok, data, resp_bin, resp
    if not zone_support then
        return false, {}
    end

    for zone, offset in pairs(TOPO_CMD_OFFSET) do
        if not zone_support[zone] then
            goto next
        end
        topo_info[zone] = {}
        ok, data = topo_read(chip, offset, TOPO_RESP_LEN_PER_ZONE)
        if not ok then
            return false, {}
        end
        for i = 1, RESP_NUM_PER_ZONE do
            if #data < i * TOPO_RESP_LEN then
                break
            end
            resp_bin = string.sub(data, (i - 1) * TOPO_RESP_LEN + 1, i * TOPO_RESP_LEN)
            resp = bs_topo_resp:unpack(resp_bin)
            -- target_port_id为0判断为无效信息
            if is_resp_valid(resp) then
                topo_info[zone][resp.port_id] = topo_info[zone][resp.port_id] and topo_info[zone][resp.port_id] or {}
                topo_info[zone][resp.port_id][resp.bcu_index] = resp
            end
        end
        ::next::
    end
    
    return ok, topo_info
end

local function run_topo_monitor(topo_monitor, conn_objs, unit_confs)
    -- read_topo_info
    local zone_support = topo_monitor.topo_reader:init_support_zone()
    topo_monitor.topo_reader.read_topo_info = function(...)
        return plugin_read_topo_info(topo_monitor.topo_reader.biz_topo_node.mds_obj.RefSmcChip, zone_support)
    end
    -- 解析bcu smc命令数据
    local _, topo_info = topo_monitor.topo_reader:read_topo_info()
    for _, config in pairs(unit_confs) do
        topo_monitor.init_config_match_info(config)
    end
    -- 先检测bcu biz connector
    for _, conn in pairs(conn_objs) do
        topo_monitor.init_biz_connector_match_info(conn)
        topo_monitor.set_topo_info_to_biz_connector(conn, topo_info, topo_monitor)
        topo_monitor.match_topo_info_with_config(conn, topo_monitor)
        -- 因为有防抖需要设置2次
        topo_monitor.update_port_status(conn, topo_monitor)
        topo_monitor.update_port_status(conn, topo_monitor)
    end

    -- 再检测unit configuration
    for _, config in pairs(unit_confs) do
        -- 因为有防抖需要设置2次
        topo_monitor.update_config_status(config, topo_monitor)
        topo_monitor.update_config_status(config, topo_monitor)
    end
end

local function run_topo_monitor_once(topo_monitor, conn_objs, unit_confs)
    -- read_topo_info
    local zone_support = topo_monitor.topo_reader:init_support_zone()
    topo_monitor.topo_reader.read_topo_info = function(...)
        return plugin_read_topo_info(topo_monitor.topo_reader.biz_topo_node.mds_obj.RefSmcChip, zone_support)
    end
    -- 解析bcu smc命令数据
    local _, topo_info = topo_monitor.topo_reader:read_topo_info()
    for _, config in pairs(unit_confs) do
        topo_monitor.init_config_match_info(config)
    end
    -- 先检测bcu biz connector
    for _, conn in pairs(conn_objs) do
        topo_monitor.init_biz_connector_match_info(conn)
        topo_monitor.set_topo_info_to_biz_connector(conn, topo_info, topo_monitor)
        topo_monitor.match_topo_info_with_config(conn, topo_monitor)
        topo_monitor.update_port_status(conn, topo_monitor)
    end

    -- 再检测unit configuration
    for _, config in pairs(unit_confs) do
        topo_monitor.update_config_status(config, topo_monitor)
    end
end

local test_common_with_two_configs_data<const> = {
    biz_topo_node = {
        {target_port_id = 49, index = 0, uid = '00000001040302023940', port_id = 9, bcu_index = 0},
        {target_port_id = 50, index = 0, uid = '00000001040302023940', port_id = 12, bcu_index = 0}
    },
    biz_conn_data = {
        {Name = 'A3a', ID = 9, Offset = 0, Width = 8}, {Name = 'A3c', ID = 10, Offset = 8, Width = 8},
        {Name = 'B3a', ID = 11, Offset = 0, Width = 8}, {Name = 'B3c', ID = 12, Offset = 8, Width = 8}
    },
    unit_conf_data = {
        {
            slot_type = 'SEU',
            slot_number = 5,
            silk_text = 'SEUSlot5',
            configs = {
                {
                    UID = '00000001040302023940',
                    Index = 0,
                    SrcPortName = {'A3a', 'A3c'},
                    TargetPortID = {49, 50},
                    Slot = {2, 3},
                    Device = {}
                }
            }
        }, {
            slot_type = 'SEU',
            slot_number = 5,
            silk_text = 'SEUSlot5',
            configs = {
                {
                    UID = '00000001040302023940',
                    Index = 0,
                    SrcPortName = {'B3a', 'B3c'},
                    TargetPortID = {49, 50},
                    Slot = {2, 3},
                    Device = {}
                }
            }
        }
    },
    uid_list = {['00000001040302023940'] = true}
}


local test_seu_with_two_configs_data<const> = {
    biz_topo_node = {
        {target_port_id = 49, index = 0, uid = '00000001040302023940', port_id = 9, bcu_index = 0},
        {target_port_id = 50, index = 0, uid = '00000001040302023940', port_id = 12, bcu_index = 0}
    },
    biz_conn_data = {
        {Name = 'A3a', ID = 9, Offset = 0, Width = 8}, {Name = 'A3c', ID = 10, Offset = 8, Width = 8},
        {Name = 'B3a', ID = 11, Offset = 0, Width = 8}, {Name = 'B3c', ID = 12, Offset = 8, Width = 8}
    },
    unit_conf_data = {
        {
            slot_type = 'SEU',
            slot_number = 5,
            silk_text = 'SEUSlot5',
            configs = {
                {
                    UID = '00000001040302023940',
                    Index = 0,
                    SrcPortName = {'A3a', 'A3c'},
                    TargetPortID = {49, 50},
                    Slot = {2, 3},
                    Device = {}
                }
            }
        }, {
            slot_type = 'SEU',
            slot_number = 5,
            silk_text = 'SEUSlot5',
            configs = {
                {
                    UID = '00000001040302023940',
                    Index = 0,
                    SrcPortName = {'B3a', 'B3c'},
                    TargetPortID = {49, 50},
                    Slot = {2, 3},
                    Device = {}
                }
            }
        }
    },
    uid_list = {['00000001040302023940'] = true},
    uid_slot_list = {
        IEU = {},
        SEU = {[5] = '00000001040302023940'}
    }
}

function TestTopoMonitor:test_seu_with_two_configs()
    -- 模拟bcu smc命令
    local biz_topo_node = create_biz_topo_node(test_seu_with_two_configs_data.biz_topo_node)
    -- 模拟bcu的biz connector
    local conn_objs = create_biz_conn_objs(test_seu_with_two_configs_data.biz_conn_data)
    -- 模拟psr的unit configuration
    local unit_confs = create_unit_conf_objs(test_seu_with_two_configs_data.unit_conf_data)

    -- 创建线缆检测对象
    local topo_monitor = c_topo_monitor.new(bus, biz_topo_node, conn_objs, unit_confs)
    -- 输入当前自发现发现的uid列表
    topo_monitor.uid_list = test_seu_with_two_configs_data.uid_list
    topo_monitor.uid_slot_list = test_seu_with_two_configs_data.uid_slot_list

    -- 运行线缆检测逻辑
    run_topo_monitor(topo_monitor, conn_objs, unit_confs)

    -- 检查数据
    lu.assertEquals(unit_confs[1].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1LinkInfo,
        '00000001040302023940(SEUSlot5)')
    lu.assertEquals(unit_confs[1].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1Status, 5)
    lu.assertEquals(unit_confs[2].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1LinkInfo,
        '00000001040302023940(SEUSlot5)')
    lu.assertEquals(unit_confs[2].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1Status, 5)
end

-- SEU线缆误告警场景：IIC线缆没插，高速线缆都插了。
local test_seu_data<const> = {
    biz_topo_node = {
        {target_port_id = 49, index = 0, uid = '00000001040302023940', port_id = 9, bcu_index = 0},
        {target_port_id = 50, index = 0, uid = '00000001040302023940', port_id = 12, bcu_index = 0}
    },
    biz_conn_data = {
        {Name = 'A3a', ID = 9, Offset = 0, Width = 8}, {Name = 'A3c', ID = 10, Offset = 8, Width = 8},
        {Name = 'B3a', ID = 11, Offset = 0, Width = 8}, {Name = 'B3c', ID = 12, Offset = 8, Width = 8}
    },
    unit_conf_data = {
        {
            slot_type = 'SEU',
            slot_number = 5,
            silk_text = 'SEUSlot5',
            configs = {
                {
                    UID = '00000001040302023940',
                    Index = 0,
                    SrcPortName = {'A3a', 'A3c'},
                    TargetPortID = {49, 50},
                    Slot = {2, 3},
                    Device = {}
                }
            }
        }, {
            slot_type = 'SEU',
            slot_number = 5,
            silk_text = 'SEUSlot5',
            configs = {
                {
                    UID = '00000001040302023940',
                    Index = 0,
                    SrcPortName = {'B3a', 'B3c'},
                    TargetPortID = {49, 50},
                    Slot = {2, 3},
                    Device = {}
                }
            }
        }
    },
    uid_list = {}
}
function TestTopoMonitor:test_seu_data()
    -- 模拟bcu smc命令
    local biz_topo_node = create_biz_topo_node(test_seu_data.biz_topo_node)
    -- 模拟bcu的biz connector
    local conn_objs = create_biz_conn_objs(test_seu_data.biz_conn_data)
    -- 模拟psr的unit configuration
    local unit_confs = create_unit_conf_objs(test_seu_data.unit_conf_data)

    -- 创建线缆检测对象
    local topo_monitor = c_topo_monitor.new(bus, biz_topo_node, conn_objs, unit_confs)
    -- 输入当前自发现发现的uid列表
    topo_monitor.uid_list = test_seu_data.uid_list

    -- 运行线缆检测逻辑
    run_topo_monitor(topo_monitor, conn_objs, unit_confs)

    -- 检查数据
    lu.assertEquals(unit_confs[1].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1LinkInfo,
        '')
    lu.assertEquals(unit_confs[1].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1Status, 0)
    lu.assertEquals(unit_confs[2].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1LinkInfo,
        '')
    lu.assertEquals(unit_confs[2].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1Status, 0)
end

-- BCU1的配置匹配成功，BCU2配置未匹配
local test_ieu_with_two_configs_data<const> = {
    BCU_SLOT = 1,
    biz_topo_node = {
        {target_port_id = 17, index = 0, uid = '00000001040302058031', port_id = 13, bcu_index = 0}, -- C4a
        {target_port_id = 18, index = 0, uid = '00000001040302058031', port_id = 17, bcu_index = 0}, -- C5a
        {target_port_id = 49, index = 0, uid = '00000001040302058031', port_id = 9, bcu_index = 0} -- A3a
    },
    biz_conn_data = {
        {Name = 'C4a', ID = 13, Offset = 0, Width = 8}, {Name = 'C5a', ID = 17, Offset = 8, Width = 8},
        {Name = 'A3a', ID = 9, Offset = 0, Width = 8}, {Name = 'A4a', ID = 34, Offset = 8, Width = 8}
    },
    unit_conf_data = {
        {
            slot_type = 'IEU',
            slot_number = 1,
            silk_text = 'IEUSlot1',
            configs = {
                {
                    UID = '00000001040302058031',
                    Index = 0,
                    BCUIndex = 1,
                    SrcPortName = {'C4a', 'C5a', 'A3a'},
                    TargetPortID = {17, 18, 49},
                    Slot = {1, 2, 3},
                    Device = {}
                }
            }
        }, {
            slot_type = 'IEU',
            slot_number = 1,
            silk_text = 'IEUSlot1',
            configs = {
                {
                    UID = '00000001040302058031',
                    Index = 0,
                    BCUIndex = 2,
                    SrcPortName = {'A4a', 'A3a'},
                    TargetPortID = {34, 33},
                    Slot = {5},
                    Device = {}
                }
            }
        }
    },
    uid_list = {['00000001040302058031'] = true},
    uid_slot_list = {
        IEU = {[1] = '00000001040302058031'},
        SEU = {}
    }
}

function TestTopoMonitor:test_ieu_with_two_bcu1()
    -- 模拟bcu smc命令
    local biz_topo_node = create_biz_topo_node(test_ieu_with_two_configs_data.biz_topo_node,
        test_ieu_with_two_configs_data.BCU_SLOT)
    -- 模拟bcu的biz connector
    local conn_objs = create_biz_conn_objs(test_ieu_with_two_configs_data.biz_conn_data, 1)
    -- 模拟psr的unit configuration
    local unit_confs = create_unit_conf_objs(test_ieu_with_two_configs_data.unit_conf_data)

    -- 创建线缆检测对象
    local topo_monitor = c_topo_monitor.new(bus, biz_topo_node, conn_objs, unit_confs)
    -- 输入当前自发现发现的uid列表
    topo_monitor.uid_list = test_ieu_with_two_configs_data.uid_list
    topo_monitor.uid_slot_list = test_ieu_with_two_configs_data.uid_slot_list

    -- 运行线缆检测逻辑
    run_topo_monitor(topo_monitor, conn_objs, unit_confs)

    -- 检查数据
    lu.assertEquals(unit_confs[1].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1LinkInfo,
        '00000001040302058031(IEUSlot1)')
    lu.assertEquals(unit_confs[1].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1Status, 1)
    lu.assertEquals(unit_confs[2].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1LinkInfo,
        '')
    lu.assertEquals(unit_confs[2].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1Status, 0)
end

local test_ieu_with_two_configs_data_absent<const> = {
    BCU_SLOT = 2,
    biz_topo_node = {
        {target_port_id = 34, index = 0, uid = '00000001040302058031', port_id = 13, bcu_index = 0} -- A4a
    },
    biz_conn_data = {
        {Name = 'C5a', ID = 17, Offset = 8, Width = 8}, {Name = 'A3a', ID = 33, Offset = 0, Width = 8},
        {Name = 'A4a', ID = 13, Offset = 8, Width = 8}
    },
    unit_conf_data = {
        {
            slot_type = 'IEU',
            slot_number = 1,
            silk_text = 'IEUSlot1',
            configs = {
                {
                    UID = '00000001040302058031',
                    Index = 0,
                    BCUIndex = 1,
                    SrcPortName = {'C4a', 'C5a', 'A3a'},
                    TargetPortID = {17, 18, 49},
                    Slot = {1, 2, 3},
                    Device = {}
                }
            }
        }, {
            slot_type = 'IEU',
            slot_number = 1,
            silk_text = 'IEUSlot1',
            configs = {
                {
                    UID = '00000001040302058031',
                    Index = 0,
                    BCUIndex = 2,
                    SrcPortName = {'A4a', 'A3a'},
                    TargetPortID = {34, 33},
                    Slot = {5},
                    Device = {}
                }
            }
        }
    },
    uid_list = {['00000001040302058031'] = true},
    uid_slot_list = {
        IEU = {[1] = '00000001040302058031'},
        SEU = {}
    }
}

-- BCU2的配置部分匹配，报线缆少插告警
function TestTopoMonitor:test_ieu_with_two_bcu2()
    -- 模拟bcu smc命令
    local biz_topo_node = create_biz_topo_node(test_ieu_with_two_configs_data_absent.biz_topo_node,
        test_ieu_with_two_configs_data_absent.BCU_SLOT)
    -- 模拟bcu的biz connector
    local conn_objs = create_biz_conn_objs(test_ieu_with_two_configs_data_absent.biz_conn_data,
        test_ieu_with_two_configs_data_absent.BCU_SLOT)
    -- 模拟psr的unit configuration
    local unit_confs = create_unit_conf_objs(test_ieu_with_two_configs_data_absent.unit_conf_data)

    -- 创建线缆检测对象
    local topo_monitor = c_topo_monitor.new(bus, biz_topo_node, conn_objs, unit_confs)
    -- 输入当前自发现发现的uid列表
    topo_monitor.uid_list = test_ieu_with_two_configs_data_absent.uid_list
    topo_monitor.uid_slot_list = test_ieu_with_two_configs_data_absent.uid_slot_list

    -- 运行线缆检测逻辑
    run_topo_monitor(topo_monitor, conn_objs, unit_confs)

    -- 检查数据
    lu.assertEquals(unit_confs[2].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1Status, 5)
    lu.assertEquals(unit_confs[2].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1LinkInfo,
        '00000001040302058031(IEUSlot1)')
end


local test_riser_with_two_configs_matched_data<const> = {
    biz_topo_node = {
        {target_port_id = 33, index = 2, uid = '00000001040302044499', port_id = 9, bcu_index = 0},
        {target_port_id = 49, index = 2, uid = '00000001040302044499', port_id = 13, bcu_index = 0}
    },
    biz_conn_data = {
        {Name = 'B3a', ID = 9, Offset = 0, Width = 8}, {Name = 'B4a', ID = 13, Offset = 0, Width = 8}
    },
    unit_conf_data = {
        {
            slot_type = 'IEU',
            slot_number = 2,
            silk_text = 'IEUSlot1',
            configs = {
                {
                    UID = '00000001040302044499',
                    Index = 2,
                    SrcPortName = {'B3a', 'B4a'},
                    TargetPortID = {33, 17},
                    Slot = {4, 5, 255},
                    Device = {}
                },
                {
                    UID = '00000001040302044499',
                    Index = 2,
                    Default = true,
                    SrcPortName = {'B3a', 'B4a'},
                    TargetPortID = {33, 49},
                    Slot = {255, 5, 6},
                    Device = {}
                }
            }
        }
    },
    uid_list = {['00000001040302044499'] = true},
    uid_slot_list = {
        IEU = {[2] = '00000001040302044499'},
        SEU = {}
    }
}

function TestTopoMonitor:test_riser_with_two_configs_matched()
    -- 模拟bcu smc命令
    local biz_topo_node = create_biz_topo_node(test_riser_with_two_configs_matched_data.biz_topo_node)
    -- 模拟bcu的biz connector
    local conn_objs = create_biz_conn_objs(test_riser_with_two_configs_matched_data.biz_conn_data)
    -- 模拟psr的unit configuration
    local unit_confs = create_unit_conf_objs(test_riser_with_two_configs_matched_data.unit_conf_data)

    -- 创建线缆检测对象
    local topo_monitor = c_topo_monitor.new(bus, biz_topo_node, conn_objs, unit_confs)
    topo_monitor.uid_list = test_riser_with_two_configs_matched_data.uid_list
    topo_monitor.uid_slot_list = test_riser_with_two_configs_matched_data.uid_slot_list
    -- 运行线缆检测逻辑
    run_topo_monitor(topo_monitor, conn_objs, unit_confs)

    -- 检查数据
    lu.assertEquals(unit_confs[1].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1Status, 1)
end

local test_riser_with_two_configs_not_present_data<const> = {
    biz_topo_node = {
        {target_port_id = 33, index = 2, uid = '00000001040302044499', port_id = 9, bcu_index = 0}
    },
    biz_conn_data = {
        {Name = 'B3a', ID = 9, Offset = 0, Width = 8}, {Name = 'B4a', ID = 13, Offset = 0, Width = 8}
    },
    unit_conf_data = {
        {
            slot_type = 'IEU',
            slot_number = 2,
            silk_text = 'IEUSlot1',
            configs = {
                {
                    UID = '00000001040302044499',
                    Index = 2,
                    SrcPortName = {'B3a', 'B4a'},
                    TargetPortID = {33, 17},
                    Slot = {4, 5, 255},
                    Device = {}
                },
                {
                    UID = '00000001040302044499',
                    Index = 2,
                    Default = true,
                    SrcPortName = {'B3a', 'B4a'},
                    TargetPortID = {33, 49},
                    Slot = {255, 5, 6},
                    Device = {}
                }
            }
        }
    },
    uid_list = {['00000001040302044499'] = true},
    uid_slot_list = {
        IEU = {[2] = '00000001040302044499'},
        SEU = {}
    }
}

function TestTopoMonitor:test_riser_with_two_configs_not_present()
    -- 模拟bcu smc命令
    local biz_topo_node = create_biz_topo_node(test_riser_with_two_configs_not_present_data.biz_topo_node)
    -- 模拟bcu的biz connector
    local conn_objs = create_biz_conn_objs(test_riser_with_two_configs_not_present_data.biz_conn_data)
    -- 模拟psr的unit configuration
    local unit_confs = create_unit_conf_objs(test_riser_with_two_configs_not_present_data.unit_conf_data)


    -- 创建线缆检测对象
    local topo_monitor = c_topo_monitor.new(bus, biz_topo_node, conn_objs, unit_confs)
    topo_monitor.uid_list = test_riser_with_two_configs_not_present_data.uid_list
    topo_monitor.uid_slot_list = test_riser_with_two_configs_not_present_data.uid_slot_list
    -- 运行线缆检测逻辑
    run_topo_monitor(topo_monitor, conn_objs, unit_confs)

    -- 检查数据
    lu.assertEquals(unit_confs[1].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1Status, 5)
end

function TestTopoMonitor:test_riser_with_two_configs_not_present_set_once()
    -- 模拟bcu smc命令
    local biz_topo_node = create_biz_topo_node(test_riser_with_two_configs_not_present_data.biz_topo_node)
    -- 模拟bcu的biz connector
    local conn_objs = create_biz_conn_objs(test_riser_with_two_configs_not_present_data.biz_conn_data)
    -- 模拟psr的unit configuration
    local unit_confs = create_unit_conf_objs(test_riser_with_two_configs_not_present_data.unit_conf_data)


    -- 创建线缆检测对象
    local topo_monitor = c_topo_monitor.new(bus, biz_topo_node, conn_objs, unit_confs)
    topo_monitor.uid_list = test_riser_with_two_configs_not_present_data.uid_list
    topo_monitor.uid_slot_list = test_riser_with_two_configs_not_present_data.uid_slot_list
    
    -- 只设置一次，防抖后设置的为默认的防抖值
    run_topo_monitor_once(topo_monitor, conn_objs, unit_confs)

    -- 检查数据
    lu.assertEquals(unit_confs[1].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1Status, 255)
end

local test_seu_with_raid_without_ubc_connection_data<const> = {
    biz_topo_node = {
        {target_port_id = 0, index = 0, uid = '00000000000000000000', port_id = 0, bcu_index = 0},
        {target_port_id = 0, index = 0, uid = '00000000000000000000', port_id = 0, bcu_index = 0}
    },
    biz_conn_data = {{Name = 'A3a', ID = 9, Offset = 0, Width = 8}, {Name = 'A3c', ID = 10, Offset = 8, Width = 8}},
    unit_conf_data = {
        {
            slot_type = 'SEU',
            slot_number = 5,
            silk_text = 'SEUSlot5',
            configs = {
                {
                    UID = '00000001040302023940',
                    Index = 0,
                    SrcPortName = {'A3a', 'A3c'},
                    TargetPortID = {49, 50},
                    Slot = {2, 3},
                    Device = {}
                }
            }
        }
    },
    uid_list = {['00000001040302023940'] = true},
    uid_slot_list = {
        IEU = {},
        SEU = {[5] = '00000001040302023940'}
    }
}

function TestTopoMonitor:test_seu_with_raid_without_ubc_connection()
    -- 模拟bcu smc命令
    local biz_topo_node = create_biz_topo_node(test_seu_with_raid_without_ubc_connection_data.biz_topo_node)
    -- 模拟bcu的biz connector
    local conn_objs = create_biz_conn_objs(test_seu_with_raid_without_ubc_connection_data.biz_conn_data)
    -- 模拟psr的unit configuration
    local unit_confs = create_unit_conf_objs(test_seu_with_raid_without_ubc_connection_data.unit_conf_data)

    -- 创建线缆检测对象
    local topo_monitor = c_topo_monitor.new(bus, biz_topo_node, conn_objs, {})
    -- 输入当前自发现发现的uid列表
    topo_monitor.uid_list = test_seu_with_raid_without_ubc_connection_data.uid_list
    topo_monitor.uid_slot_list = test_seu_with_two_configs_data.uid_slot_list

    -- 运行线缆检测逻辑
    run_topo_monitor(topo_monitor, conn_objs, unit_confs)

    -- 检查数据
    lu.assertEquals(unit_confs[1].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1LinkInfo,
        '00000001040302023940(SEUSlot5)')
    lu.assertEquals(unit_confs[1].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1Status, 255)

    topo_monitor.bios_complete_count = 10
    run_topo_monitor(topo_monitor, conn_objs, unit_confs)
    lu.assertEquals(unit_confs[1].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1Status, 5)

    topo_monitor.has_raid = true
    run_topo_monitor(topo_monitor, conn_objs, unit_confs)
    lu.assertEquals(unit_confs[1].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1Status, 1)
end

local test_smc_with_ff_data<const> = {
    biz_topo_node = {
        {target_port_id = 0xff, index = 0xff, uid = string.rep('\xFF', 20), port_id = 0xff, bcu_index = 0},
        {target_port_id = 0xff, index = 0xff, uid = string.rep('\xFF', 20), port_id = 0xff, bcu_index = 0}
    },
    biz_conn_data = {
        {Name = 'A3a', ID = 9, Offset = 0, Width = 8}, {Name = 'A3c', ID = 10, Offset = 8, Width = 8},
        {Name = 'B3a', ID = 11, Offset = 0, Width = 8}, {Name = 'B3c', ID = 12, Offset = 8, Width = 8}
    },
    unit_conf_data = {
        {
            slot_type = 'SEU',
            slot_number = 5,
            silk_text = 'SEUSlot5',
            configs = {
                {
                    UID = '00000001040302023940',
                    Index = 0,
                    SrcPortName = {'A3a', 'A3c'},
                    TargetPortID = {49, 50},
                    Slot = {2, 3},
                    Device = {}
                }
            }
        }, {
            slot_type = 'SEU',
            slot_number = 5,
            silk_text = 'SEUSlot5',
            configs = {
                {
                    UID = '00000001040302023940',
                    Index = 0,
                    SrcPortName = {'B3a', 'B3c'},
                    TargetPortID = {49, 50},
                    Slot = {2, 3},
                    Device = {}
                }
            }
        }
    },
    uid_list = {['00000001040302023940'] = true},
    uid_slot_list = {
        IEU = {},
        SEU = {[5] = '00000001040302023940'}
    }
}

function TestTopoMonitor:test_smc_with_ff()
    -- 模拟bcu smc命令
    local biz_topo_node = create_biz_topo_node(test_smc_with_ff_data.biz_topo_node)
    -- 模拟bcu的biz connector
    local conn_objs = create_biz_conn_objs(test_smc_with_ff_data.biz_conn_data)
    -- 模拟psr的unit configuration
    local unit_confs = create_unit_conf_objs(test_smc_with_ff_data.unit_conf_data)
    
    -- 创建线缆检测对象
    local topo_monitor = c_topo_monitor.new(bus, biz_topo_node, conn_objs, unit_confs)
    -- 输入当前自发现发现的uid列表
    topo_monitor.uid_list = test_smc_with_ff_data.uid_list
    topo_monitor.uid_slot_list = test_smc_with_ff_data.uid_slot_list
    -- 运行线缆检测逻辑
    run_topo_monitor(topo_monitor, conn_objs, unit_confs)

    -- 检查数据，UnitConf显示未连接
    lu.assertEquals(unit_confs[1].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1Status, 255)
    lu.assertEquals(unit_confs[1].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1LinkInfo,
        '00000001040302023940(SEUSlot5)')
    lu.assertEquals(unit_confs[2].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1Status, 255)
    lu.assertEquals(unit_confs[2].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1LinkInfo,
        '00000001040302023940(SEUSlot5)')
    -- 检查数据，BizConn显示未连接
    lu.assertEquals(conn_objs[1].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1LinkInfo, '')
    lu.assertEquals(conn_objs[1].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1Status, 0)
    lu.assertEquals(conn_objs[2].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1LinkInfo, '')
    lu.assertEquals(conn_objs[2].mds_obj['bmc.kepler.Systems.UnitConfigError'].Port1Status, 0)
end

function TestTopoMonitor:test_deallocate_ubc_error()
    local topo_reader = c_topo_reader.new()
    topo_reader.zone_support = {'A', 'B'}
    local ubc_error_info = tbl_cache:allocate()
    ubc_error_info['A'] = tbl_cache:allocate()
    ubc_error_info['B'] = tbl_cache:allocate()
    local ok = pcall(function ()
        topo_reader:deallocate_ubc_error(ubc_error_info)
    end)
    lu.assertEquals(ok, true)
end

function TestTopoMonitor:test_clearTable()
    local tbl = 1
    local _, ret = pcall(c_topo_monitor.clearTable, c_topo_monitor, tbl)
    lu.assertEquals(ret, nil)
    _, ret = pcall(c_topo_monitor.clearTable, c_topo_monitor)
    lu.assertEquals(ret, nil)
    local sub_tbl = {'test'}
    tbl = {sub_tbl}
    local ok = pcall(c_topo_monitor.clearTable, c_topo_monitor, tbl)
    lu.assertEquals(ok, true)
    lu.assertEquals(next(tbl) == nil, true)
    lu.assertEquals(next(sub_tbl) == nil, true)
end

function TestTopoMonitor:test_init_solid_config()
    c_topo_monitor.solid_config = {}
    c_topo_monitor.unit_configs = {
        [1] = {
            ["configs"] = {
                [1] = {
                    ["uid"] = "uid1",
                    ["target_ports_id"] = {11}
                }
            },
            get_prop = function()
                return ""
            end
        }
    }
    pcall(c_topo_monitor.init_solid_config, c_topo_monitor)
    lu.assertEquals(c_topo_monitor.solid_config["uid1_0"].target_ports_largest_subset, {11})
end

function TestTopoMonitor:test_update_solid_config()
    c_topo_monitor.solid_config = {
        ["uid1_1"] = {
            exist_ports = {}, -- key是target_port_id
            lack_ports = {}, -- key是target_port_id
            target_ports_lists = {}, 
            target_ports_largest_subset = {11},
            silk_text = "silk_text"
        }
    }
    pcall(c_topo_monitor.update_solid_config, c_topo_monitor)
    lu.assertEquals(c_topo_monitor.solid_config["uid1_1"].lack_ports[11], true)
end

function TestTopoMonitor:test_update_solid_config_status()
    local unit_config = {
        ["configs"] = {
            [1] = {
                ["uid"] = "uid1",
                ["target_ports_id"] = {11}
            }
        },
        get_prop = function()
            return 1
        end
    }
    local config_uid = "uid1"
    local config_bcu_index = 0
    local silk_text = "silk_text"
    local config_status = 5
    local ret
    c_topo_monitor.biz_connectors = {
        [1] = {
            is_pcie_up_biz_connector = function()
                return true
            end,
            ["container_uid"] = "uid1",
            ["container_slot"] = 1,
            ["target_port_id"] = 11,
            ["monitor_status"] = {
                ["Port1LinkInfo"] = {
                    get_debounced_val = function()
                        return 5
                    end
                }
            },
            set_prop = function(self, k, v)
                ret = v
            end
        }
    }
    c_topo_monitor.solid_config = {
        ["uid1_0"] = {
            exist_ports = {}, -- key是target_port_id
            lack_ports = {
                [11] = true
            }, -- key是target_port_id
            target_ports_lists = {}, 
            target_ports_largest_subset = {11},
            silk_text = "silk_text"
        }
    }
    pcall(c_topo_monitor.update_solid_config_status, c_topo_monitor, unit_config, config_uid, config_bcu_index,
        silk_text, config_status)
    lu.assertEquals(ret, 5)
end

function TestTopoMonitor:test_uptree()
    local sleep_bak = cmn.skynet.sleep
    local fork_loop_bak = cmn.skynet.fork_loop
    local init_solid_config_bak = c_topo_monitor.init_solid_config
    local check_component_health_bak = c_topo_monitor.check_component_health
    local update_env_bak = c_topo_monitor.update_env
    local set_topo_info_to_biz_connector_bak = c_topo_monitor.set_topo_info_to_biz_connector
    local match_topo_info_with_config_bak = c_topo_monitor.match_topo_info_with_config
    local update_port_status_bak = c_topo_monitor.update_port_status
    local update_config_status_bak = c_topo_monitor.update_config_status
    local topo_reader_bak = c_topo_monitor.topo_reader
    local biz_connectors_bak = c_topo_monitor.biz_connectors
    local unit_configs_bak = c_topo_monitor.unit_configs
    local ubc_monitor_bak = c_topo_monitor.ubc_monitor
    cmn.skynet.sleep = function(t)
        if t == 6000 then
            error('break')
        end
    end
    cmn.skynet.fork_loop = function(_, cb)
        cb()
    end
    c_topo_monitor.init_solid_config = function()
    end
    c_topo_monitor.check_component_health = function()
        return true
    end
    c_topo_monitor.ubc_monitor = function()
    end
    c_topo_monitor.update_env = function()
    end
    c_topo_monitor.set_topo_info_to_biz_connector = function()
    end
    c_topo_monitor.match_topo_info_with_config = function()
    end
    c_topo_monitor.update_port_status = function()
    end
    c_topo_monitor.update_config_status = function()
    end
    
    c_topo_monitor.topo_reader = {
        read_topo_info = function()
            return {1}
        end
    }

    local unit_mds_obj = {
        ['bmc.kepler.Systems.UnitConfigError'] = {Port1LinkInfo = 'xxx'},
        Configurations = {
            {
                UID = '00000001040302023940',
                Index = 1,
                SrcPortName = {'A3a', 'A3c'},
                TargetPortID = {49, 50},
                Slot = {2, 3},
                Device = {0, 1}
            }
        },
        SlotSilkText = 'SlotSilkText',
        register_mdb_objects = function()
        end
    }
    local unit_object = c_unit_configuration.new(unit_mds_obj, 'unit_position')

    local mds_obj = {
        ObjectName = 'ObjectName',
        register_mdb_objects = function()
        end
    }
    local conn_object = c_business_connector.new(mds_obj, 'conn_position', self.mock_complete_signal)
    c_topo_monitor.pcie_addr_info_list = {}
    c_topo_monitor.biz_connectors = {conn_object}
    c_topo_monitor.unit_configs = {unit_object}
    pcall(function()
        c_topo_monitor.main(c_topo_monitor)
    end)

    lu.assertEquals(unit_object.uptree_flag, true)
    lu.assertEquals(conn_object.uptree_flag, true)

    cmn.skynet.sleep = sleep_bak 
    cmn.skynet.fork_loop = fork_loop_bak 
    c_topo_monitor.init_solid_config = init_solid_config_bak
    c_topo_monitor.check_component_health = check_component_health_bak
    c_topo_monitor.update_env = update_env_bak 
    c_topo_monitor.set_topo_info_to_biz_connector = set_topo_info_to_biz_connector_bak
    c_topo_monitor.match_topo_info_with_config = match_topo_info_with_config_bak
    c_topo_monitor.update_port_status = update_port_status_bak
    c_topo_monitor.update_config_status = update_config_status_bak
    c_topo_monitor.topo_reader = topo_reader_bak
    c_topo_monitor.biz_connectors = biz_connectors_bak
    c_topo_monitor.unit_configs = unit_configs_bak
    c_topo_monitor.ubc_monitor = ubc_monitor_bak
end

function TestTopoMonitor:test_update_uid_slot_list()
    c_topo_monitor.pcie_addr_info_list = {
        {
            get_prop = function(_, prop)
                if prop == 'ContainerSlot' then
                    return 1
                elseif prop == 'ContainerUnitType' then
                    return 'IEU'
                elseif prop == 'ContainerUID' then
                    return 'ContainerUID'
                end

            end
        }
    }
    c_topo_monitor.uid_slot_list = {
        IEU = {},
        SEU = {}
    }
    c_topo_monitor.update_uid_slot_list(c_topo_monitor)
    lu.assertEquals(c_topo_monitor.uid_slot_list['IEU'][1], 'ContainerUID')
    c_topo_monitor.pcie_addr_info_list = {}
end
    