-- 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 def = require 'biz_topo.def'
local comm = require 'common'
local c_biz_topo_service = require 'biz_topo.biz_topo_service'
local ipmi = require 'ipmi'
local cc = ipmi.types.Cc
local c_device_service = require 'device.device_service'
local mdb = require 'mc.mdb'
local cmn = require 'common'
local bs = require 'mc.bitstring'

TestBizTopoService = {}

local pcie_card_list = {
    [1] = "test_pcie",
    [2] = "test_pcie"
}
local ocp_card_list = {
    [3] = "test_ocp",
    [4] = "test_ocp"
}

local test_topo_service = {
    device_loader = {
        persistent_load_info = {
            [def.device_type.PCIE_CARD] = pcie_card_list,
            [def.device_type.OCP_CARD] = ocp_card_list
        }
    }
}

-- 测试套间执行前调用
function TestBizTopoService:setupClass()
end

-- 测试套件执行后调用
function TestBizTopoService:teardownClass()
end

function TestBizTopoService:test_parse_pcie_card_bdf_data()

end

function TestBizTopoService:test_get_device_present_status()
    -- 1. 测试非法场景 非法的devtype
    local req = {
        DeviceType = 0x3,
    }
    local present = c_biz_topo_service.get_device_present_status(test_topo_service, req)
    lu.assertIsNil(present)

    -- 2. 测试正常场景 获取PCIeCard 在位状态
    req.DeviceType = comm.ipmi_type.IPMI_PCIE_CARD_TYPE
    present = c_biz_topo_service.get_device_present_status(test_topo_service, req)
    lu.assertEquals(present, 3)

    -- 3. 测试正常场景 获取OCPCard 在位状态
    req.DeviceType = comm.ipmi_type.IPMI_OCP_CARD_TYPE
    present = c_biz_topo_service.get_device_present_status(test_topo_service, req)
    lu.assertEquals(present, 12)
end

function TestBizTopoService:test_update_integrated_pcie_config()
    --测试tail长度与DataLen不一致的异常场景
    local req = {
        DataLen = 1,
        Tail = '123'
    }
    local context = {
        ChanType = 1
    }
    local ok, _ = pcall(function()
        c_biz_topo_service.update_integrated_pcie_config(test_topo_service, req, context)
    end)
    lu.assertEquals(ok, false)
end

function TestBizTopoService:test_update_bdf_cfgs()
    local config = {
        socket = 1,
        bus = 5,
        controller_index  = 2,
        controller_type  = 7,
    }
    local biz_topo_data = {
        bdfconfigs = {
            [1] = {
                mds_obj = {
                    BDFConfigs = {
                        {1,2,3,4,5,6,7}
                    }
                },
                config_new = function()
                    return { PortId = 1, ControllerIndex = 2, Segment = 3,
                        Bus = 4, Device = 5, Function = 6, Type = 7 }
                end
            }
        },
        init_bdf_map = function()
        end
    }
    c_biz_topo_service.update_bdf_cfgs(biz_topo_data, config)
    lu.assertEquals(biz_topo_data.bdfconfigs[1].mds_obj.BDFConfigs, {{1, 2, 3, 5, 5, 6, 7}})
    local config_2 = {
        socket = 1,
        bus = 5,
        controller_index  = 3,
        controller_type  = 7,
    }
    c_biz_topo_service.update_bdf_cfgs(biz_topo_data, config_2)
    lu.assertEquals(biz_topo_data.bdfconfigs[1].mds_obj.BDFConfigs, {{1, 2, 3, 5, 5, 6, 7}})
end

function TestBizTopoService:test_get_exu_smc()
    local mdb_bak = mdb.get_object

    mdb.get_object = function()
        return 1
    end
    local smc = c_biz_topo_service.get_exu_smc(c_biz_topo_service)
    lu.assertEquals(smc, 1)

    mdb.get_object = function()
        error('break')
    end
    local smc = c_biz_topo_service.get_exu_smc(c_biz_topo_service)
    lu.assertEquals(smc, nil)

    mdb.get_object = mdb_bak
end

function TestBizTopoService:test_monitor_ocp_presence()
    local c_biz_topo_service_bak = c_biz_topo_service
    local sleep_bak = cmn.skynet.sleep
    cmn.skynet.fork = function(cb)
        cb()
    end
    cmn.skynet.sleep = function()
        error('break')
    end

    c_biz_topo_service.monitor_ocp_task = true
    local ok, _ = pcall(function()
        c_biz_topo_service.monitor_ocp_presence(c_biz_topo_service)
    end)
    lu.assertEquals(ok, true)

    c_biz_topo_service.monitor_ocp_task = false
    c_biz_topo_service.device_loader = {
        ocp_load_from_tc = function()
        end
    }
    ok, _ = pcall(function()
        c_biz_topo_service.monitor_ocp_presence(c_biz_topo_service)
    end)
    lu.assertEquals(ok, false)

    cmn.skynet.sleep = sleep_bak
    c_biz_topo_service = c_biz_topo_service_bak
end

function TestBizTopoService:test_update_bcu_src_conn_ready()
    cmn.skynet.fork = function(cb)
        cb()
    end
    cmn.skynet.sleep = function()
    end
    local biz_topo = {
        src_biz_connector_container = {}
    }
    local signal_mgr = {
        get = function()
            return {
                emit = function()
                end
            }
        end
    }
    c_biz_topo_service.biz_topo = biz_topo
    c_biz_topo_service.signal_mgr = signal_mgr
    c_biz_topo_service:update_bcu_src_conn_ready()
    lu.assertEquals(c_biz_topo_service.bcu_src_conn_ready, true)
end

function TestBizTopoService:test_get_topo_info()
    c_biz_topo_service.queue = function(func)
    end
    
    c_biz_topo_service.bus = {
        pcall = function()
        end
    }

    local PCIeTopoInfo = '<<status:1/unit:8, segment:1/unit:8, socket:1/unit:8, ' ..
        'bus:1/unit:8, device:1/unit:8, func:1/unit:8, group:1/unit:8, component:1/unit:8, slot:1/unit:8, '..
        'vendor_id:2/little-unit:8, dev_id:2/little-unit:8, '..
        'sec_bus:1/unit:8, sub_bus:1/unit:8, bdf_no:1/unit:8, resv1:1/unit:8, '..
        'low_base:4/little-unit:8, low_limit:4/little-unit:8, high_base:8/little-unit:8, high_limit:8/little-unit:8, '..
        'resv2:2/little-unit:8, resv4:4/little-unit:8, update_flag:1/unit:8, mmio_cnt:1/unit:8, '..
        'mmio0_base:8/little-unit:8, mmio0_limit:8/little-unit:8, '..
        'mmio1_base:8/little-unit:8, mmio1_limit:8/little-unit:8, '..
        'mmio2_base:8/little-unit:8, mmio2_limit:8/little-unit:8, '..
        'mmio3_base:8/little-unit:8, mmio3_limit:8/little-unit:8, '..
        'mmio4_base:8/little-unit:8, mmio4_limit:8/little-unit:8, '..
        'mmio5_base:8/little-unit:8, mmio5_limit:8/little-unit:8>>'

    local pcie_topo = {
        status = 2,
        segment = 0,
        socket = 0,
        bus = 0,
        device = 4,
        func = 0,
        group = 1,
        component = 255,
        slot = 255,
        vendor_id = 6629,
        dev_id = 41248,
        sec_bus = 3,
        sub_bus = 3,
        bdf_no = 0,
        resv1 = 0,
        low_base = 3489660928,
        low_limit = 3758096384,
        high_base = 0,
        high_limit = 0,
        resv2 = 0,
        resv4 = 0,
        update_flag = 0,
        mmio_cnt = 6,
        mmio0_base = 3829399552,
        mmio0_limit = 3831496703,
        mmio1_base = 879609322208,
        mmio1_limit = 87960955119359,
        mmio2_base = 0,
        mmio2_limit = 0,
        mmio3_base = 0,
        mmio3_limit = 0,
        mmio4_base = 0,
        mmio4_limit = 0,
        mmio5_base = 0,
        mmio5_limit = 0
    }
    
    local req_data = bs.new(PCIeTopoInfo):pack(pcie_topo)

    local req = {
        ManufactureId = string.char(0xdb, 0x07, 0x00),
        SubCommand = string.char(0x70),
        ActionType = string.char(0x05),
        Length = string.char(0x10),
        Data = req_data
    }

    local property = {
        save = function()
        end
    }

    c_biz_topo_service.local_db = {
        SystemsPCIeTopoInfo = function()
            return property
        end
    }

    c_device_service.queue = function(func, ...)
        func(...)
    end

    c_device_service.bus = {
        pcall = function(...)
        end
    }

    c_biz_topo_service:set_pcie_topo_info(req, "context")

    lu.assertEquals(property.CfgLowBase, 3489660928)
    lu.assertEquals(property.VendorId, 6629)

    property.save = function()
        error("Save topo error test.")
    end
    local ok, _ = pcall(function()
        c_biz_topo_service:set_pcie_topo_info(req, "context")
    end)
    lu.assertEquals(ok, false)

    property.save = function()
    end

    pcie_topo.status = 1
    pcie_topo.low_base = 1
    req_data = bs.new(PCIeTopoInfo):pack(pcie_topo)
    req = {
        ManufactureId = string.char(0xdb, 0x07, 0x00),
        SubCommand = string.char(0x70),
        ActionType = string.char(0x05),
        Length = string.char(0x10),
        Data = req_data
    }
    c_biz_topo_service:set_pcie_topo_info(req, "context")
    lu.assertEquals(property.CfgLowBase, 3489660928)
end

function TestBizTopoService:test_get_multi_systems_topo_info()
    c_biz_topo_service.queue = function(func)
    end

    c_biz_topo_service.bus = {
        pcall = function()
            error("Try send failed")
        end
    }

    local PCIeTopoInfo = '<<global_system_id:1/unit:8, local_system_id:1/unit:8,' ..
        'status:1/unit:8, segment:1/unit:8, socket:1/unit:8, ' ..
        'bus:1/unit:8, device:1/unit:8, func:1/unit:8, group:1/unit:8, component:1/unit:8, slot:1/unit:8, '..
        'vendor_id:2/little-unit:8, dev_id:2/little-unit:8, '..
        'sec_bus:1/unit:8, sub_bus:1/unit:8, bdf_no:1/unit:8, resv1:1/unit:8, '..
        'low_base:4/little-unit:8, low_limit:4/little-unit:8, high_base:8/little-unit:8, high_limit:8/little-unit:8, '..
        'resv2:2/little-unit:8, resv4:4/little-unit:8, update_flag:1/unit:8, mmio_cnt:1/unit:8, '..
        'mmio0_base:8/little-unit:8, mmio0_limit:8/little-unit:8, '..
        'mmio1_base:8/little-unit:8, mmio1_limit:8/little-unit:8, '..
        'mmio2_base:8/little-unit:8, mmio2_limit:8/little-unit:8, '..
        'mmio3_base:8/little-unit:8, mmio3_limit:8/little-unit:8, '..
        'mmio4_base:8/little-unit:8, mmio4_limit:8/little-unit:8, '..
        'mmio5_base:8/little-unit:8, mmio5_limit:8/little-unit:8>>'

    local pcie_topo = {
        global_system_id = 1,
        local_system_id = 0,
        status = 2,
        segment = 0,
        socket = 0,
        bus = 0,
        device = 4,
        func = 0,
        group = 1,
        component = 255,
        slot = 255,
        vendor_id = 6629,
        dev_id = 41248,
        sec_bus = 3,
        sub_bus = 3,
        bdf_no = 0,
        resv1 = 0,
        low_base = 3489660928,
        low_limit = 3758096384,
        high_base = 0,
        high_limit = 0,
        resv2 = 0,
        resv4 = 0,
        update_flag = 0,
        mmio_cnt = 6,
        mmio0_base = 3829399552,
        mmio0_limit = 3831496703,
        mmio1_base = 879609322208,
        mmio1_limit = 87960955119359,
        mmio2_base = 0,
        mmio2_limit = 0,
        mmio3_base = 0,
        mmio3_limit = 0,
        mmio4_base = 0,
        mmio4_limit = 0,
        mmio5_base = 0,
        mmio5_limit = 0
    }
    
    local req_data = bs.new(PCIeTopoInfo):pack(pcie_topo)

    local req = {
        ManufactureId = string.char(0xdb, 0x07, 0x00),
        SubCommand = string.char(0x70),
        ActionType = string.char(0x14),
        Length = string.char(0x10),
        Data = req_data
    }

    local property = {
        save = function()
        end
    }

    c_biz_topo_service.local_db = {
        SystemsPCIeTopoInfo = function()
            return property
        end
    }

    c_device_service.queue = function(func, ...)
        func(...)
    end

    c_device_service.bus = {
        pcall = function(...)
        end
    }

    c_biz_topo_service:set_multi_systems_pcie_topo_info(req, "context")
    lu.assertEquals(property.CfgLowBase, 3489660928)
    lu.assertEquals(property.VendorId, 6629)

    property.save = function()
        error("Save topo error test.")
    end
    local ok, _ = pcall(function()
        c_biz_topo_service:set_multi_systems_pcie_topo_info(req, "context")
    end)
    lu.assertEquals(ok, false)

    property.save = function()
    end    

    pcie_topo.status = 1
    pcie_topo.low_base = 1
    req_data = bs.new(PCIeTopoInfo):pack(pcie_topo)
    req = {
        ManufactureId = string.char(0xdb, 0x07, 0x00),
        SubCommand = string.char(0x70),
        ActionType = string.char(0x05),
        Length = string.char(0x10),
        Data = req_data
    }
    c_biz_topo_service:set_multi_systems_pcie_topo_info(req, "context")
    lu.assertEquals(property.CfgLowBase, 3489660928)
end