-- 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_pcie_device = require 'device.class.pcie_device'
local c_ocp_device = require 'device.class.ocp_card'
local cmn = require 'common'
local ipmi = require 'ipmi'

local PCIECARD_INTERFACE <const> = 'bmc.kepler.Systems.PCIeDevices.PCIeCard'

TestOCPCard = {}

function TestOCPCard:test_set_prop()
    local position = 'position'
    local mdb_obj = {
        LocalProp = 1,
        SameProp = 1,
        [PCIECARD_INTERFACE] = {
            InfProp = 1,
            SameProp = 2
        },
        property_changed = {
            on = function()
            end
        }
    }
    local bus = {}
    local reset_local_db = {}
    local ocp_obj = c_ocp_device.new(mdb_obj, position, bus, reset_local_db)

    lu.assertEquals(ocp_obj:get_prop('SameProp'), 1)
    lu.assertEquals(ocp_obj:get_prop('SameProp', PCIECARD_INTERFACE), 2)
    lu.assertEquals(ocp_obj:get_prop('LocalProp'), 1)
    lu.assertEquals(ocp_obj:get_prop('InfProp', PCIECARD_INTERFACE), 1)
    -- 如果本地属性中不存在，从默认Interface属性中查找
    lu.assertEquals(ocp_obj:get_prop('InfProp'), 1)

    ocp_obj:set_prop('SameProp', 10)
    lu.assertEquals(ocp_obj:get_prop('SameProp'), 10)
    lu.assertEquals(ocp_obj:get_prop('SameProp', PCIECARD_INTERFACE), 2)
    ocp_obj:set_prop('SameProp', 11, PCIECARD_INTERFACE)
    lu.assertEquals(ocp_obj:get_prop('SameProp'), 10)
    lu.assertEquals(ocp_obj:get_prop('SameProp', PCIECARD_INTERFACE), 11)
    ocp_obj:set_prop('LocalProp', 12)
    lu.assertEquals(ocp_obj:get_prop('LocalProp'), 12)
    ocp_obj:set_prop('InfProp', 13, PCIECARD_INTERFACE)
    lu.assertEquals(ocp_obj:get_prop('InfProp'), 13, PCIECARD_INTERFACE)
    -- 如果本地属性中不存在，从默认Interface属性中查找
    ocp_obj:set_prop('InfProp', 14)
    lu.assertEquals(ocp_obj:get_prop('InfProp'), 14)
    lu.assertEquals(ocp_obj:get_prop('InfProp'), 14, PCIECARD_INTERFACE)
    ocp_obj.mds = nil
    lu.assertEquals(ocp_obj:get_prop('InfProp'), nil)
    lu.assertEquals(ocp_obj:set_prop('InfProp', 14), nil)
end

function TestOCPCard:test_task_update_pcb_version()
    cmn.skynet.fork = function(cb)
        cb()
    end
    cmn.skynet.sleep = function(cb)
    end
    local position = 'position'
    local mdb_obj = {
        PcbID = 1,
        PcbVersion = '',
        property_changed = {
            on = function()
            end
        }
    }
    local bus = {}
    local reset_local_db = {}
    local ocp_obj = c_ocp_device.new(mdb_obj, position, bus, reset_local_db)

    ocp_obj.fetch_pcb_flag = false
    ocp_obj:task_update_pcb_version()
    ocp_obj.fetch_pcb_flag = true
    ocp_obj:task_update_pcb_version()

    ocp_obj:set_prop('PcbID', 0)
    ocp_obj.fetch_pcb_flag = true
    ocp_obj:task_update_pcb_version()

    ocp_obj:set_prop('PcbID', 27)
    ocp_obj.fetch_pcb_flag = true
    ocp_obj:task_update_pcb_version()
end

function TestOCPCard:test_update_pcie_vpd_info()
    local position = 'position'
    local mdb_obj = {
        PcbID = 1,
        PcbVersion = '',
        SerialNumber = '',
        PartNumber = '',
        property_changed = {
            on = function()
            end
        }
    }
    local bus = {}
    local reset_local_db = {}
    local ocp_obj = c_ocp_device.new(mdb_obj, position, bus, reset_local_db)
    local pcie_device_obj = c_pcie_device.new({
        DeviceName = "pcie-device",
        DeviceType = 8,
        SocketID = 1,
        DevBus = 150,
        DevDevice = 0,
        DevFunction = 0,
        BaseClassCode = 0,
        SubClassCode = 0,
        ProgrammingInterface = 0,
        MultihostPresence = 1,
        path = '/bmc/kepler/Systems/1',
        property_changed = {
            on = function()
            end
        }
    }, "123", {})

    ocp_obj.reset_local_db.select = function()
        return {
            where = function()
                return {
                    first = function()
                        return {
                            SerialNumber = '1',
                            PartNumber = '2',
                        }
                    end
                }
            end
        }
    end
    ocp_obj:update_pcie_vpd_info(pcie_device_obj)
    lu.assertEquals(ocp_obj:get_prop('SerialNumber'), '1')
    ocp_obj.reset_local_db.select = function()
        return {
            where = function()
                return {
                    first = function()
                        return nil
                    end
                }
            end
        }
    end
    ocp_obj:update_pcie_vpd_info(pcie_device_obj)
end

function TestOCPCard:test_get_pcie_lang_info()
    local position = 'position'
    local mdb_obj = {
        PcbID = 1,
        PcbVersion = '',
        SerialNumber = '',
        PartNumber = '',
        CardForServers = {},
        LinkSpeed = '',
        LinkSpeedCapability = '',
        LinkWidth = '',
        LinkWidthAbility = '',
        property_changed = {
            on = function()
            end
        }
    }
    local bus = {}
    local reset_local_db = {}
    local ocp_obj = c_ocp_device.new(mdb_obj, position, bus, reset_local_db)
    local pcie_device_obj = c_pcie_device.new({
        DeviceName = "pcie-device",
        DeviceType = 8,
        SocketID = 1,
        DevBus = 150,
        DevDevice = 0,
        DevFunction = 0,
        BaseClassCode = 0,
        SubClassCode = 0,
        ProgrammingInterface = 0,
        MultihostPresence = 1,
        path = '/bmc/kepler/Systems/1',
        property_changed = {
            on = function()
            end
        }
    }, "123", {})

    ipmi.request = function()
        return 0, "\x00\x07\xdb\x10\x02\x03\x04\x05\x06\x07"
    end
    ocp_obj:get_pcie_lang_info(pcie_device_obj)

    pcie_device_obj.mds = {
        property_changed = {
            on = function()
                return
            end
        }
    }
    ocp_obj:sync_info_to_card(pcie_device_obj)
end

function TestOCPCard:test_pcie_get_capid_addr()
    local position = 'position'
    local mdb_obj = {
        PcbID = 1,
        PcbVersion = '',
        SerialNumber = '',
        PartNumber = '',
        CardForServers = {},
        LinkSpeed = '',
        LinkSpeedCapability = '',
        LinkWidth = '',
        LinkWidthAbility = '',
        property_changed = {
            on = function()
            end
        }
    }
    local bus = {}
    local reset_local_db = {}
    local ocp_obj = c_ocp_device.new(mdb_obj, position, bus, reset_local_db)

    local c_request = ipmi.request
    ipmi.request = function()
        return 0xff
    end
    local pcie_info = {
        is_local = 1,
        cpu_id = 1,
        address = 1,
        function_num = 1,
        device_num = 1,
        bus_num = 1,
        read_length = 4
    }
    local ok = ocp_obj:pcie_get_capid_addr(pcie_info)
    ipmi.request = c_request
    lu.assertEquals(ok, nil)
end

function TestOCPCard:test_get_pcie_lang_info()
    local position = 'position'
    local mdb_obj = {
        PcbID = 1,
        PcbVersion = '',
        SerialNumber = '',
        PartNumber = '',
        CardForServers = {},
        LinkSpeed = '',
        LinkSpeedCapability = '',
        LinkWidth = '',
        LinkWidthAbility = '',
        property_changed = {
            on = function()
            end
        }
    }
    local bus = {}
    local reset_local_db = {}
    local ocp_obj = c_ocp_device.new(mdb_obj, position, bus, reset_local_db)

    local c_pcie_get_capid_addr = c_ocp_device.pcie_get_capid_addr
    c_ocp_device.pcie_get_capid_addr = function()
        return false
    end
    local pcie_device_obj = {
        get_prop = function()
            return true
        end
    }
    local ok = ocp_obj:get_pcie_lang_info(pcie_device_obj)
    c_ocp_device.pcie_get_capid_addr = c_pcie_get_capid_addr
    lu.assertEquals(ok, 1)
end

function TestOCPCard:test_sync_info_to_card()
    local obj = c_ocp_device.new({
        LocalProp = 1,
        SameProp = 1,
        [PCIECARD_INTERFACE] = {
            InfProp = 1,
            SameProp = 2
        },
        property_changed = {
            on = function()
            end
        }
    }, "", {}, {})
    local ocp_device_obj = {}
    ocp_device_obj.mds = {}
    ocp_device_obj.mds.property_changed = {
        on = function (_, func_prop_changed)
            pcall(func_prop_changed, 'MultihostPresence', 1, {})
        end
    }
    ocp_device_obj.get_prop = function ()
        return ''
    end
    ocp_device_obj.set_prop = function ()
        return true
    end
    local ok = pcall(function ()
        obj:sync_info_to_card(ocp_device_obj)
    end)
    lu.assertEquals(ok, true)
end

function TestOCPCard:test_update_board_id()
    local pcie_addr_obj = {
        ['get_prop'] = function()
            return 'test'
        end
    }
    local ocp_obj = {
        ['set_prop'] = function()
            return 'set'
        end
    }
    local ret = c_ocp_device.update_vid_did(ocp_obj, pcie_addr_obj)
    lu.assertEquals(ret, nil)
    local ret = c_ocp_device.update_description(ocp_obj, "card")
    lu.assertEquals(ret, nil)
end

function TestOCPCard:test_check_count()
    c_ocp_device.check_count = 0
    c_ocp_device:add_check_count()
    local ret = c_ocp_device:need_check()
    lu.assertEquals(ret, true)

    c_ocp_device:add_check_count()
    c_ocp_device:add_check_count()
    ret = c_ocp_device:need_check()
    lu.assertEquals(ret, false)
end