-- 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_card = require 'device.class.pcie_card'
local ipmi = require 'ipmi'
local client = require 'pcie_device.client'

TestPcieCard = {}

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

function TestPcieCard:test_set_prop()
    c_pcie_card.mds = {
        LocalProp = 1,
        SameProp = 1,
        [PCIECARD_INTERFACE] = {
            InfProp = 1,
            SameProp = 2
        },
        property_changed = {
            on = function()
            end
        }
    }
    lu.assertEquals(c_pcie_card:get_prop('SameProp'), 1)
    lu.assertEquals(c_pcie_card:get_prop('SameProp', PCIECARD_INTERFACE), 2)
    lu.assertEquals(c_pcie_card:get_prop('LocalProp'), 1)
    lu.assertEquals(c_pcie_card:get_prop('InfProp', PCIECARD_INTERFACE), 1)
    -- 如果本地属性中不存在，从默认Interface属性中查找
    lu.assertEquals(c_pcie_card:get_prop('InfProp'), 1)

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

function TestPcieCard:test_pcie_get_capid_addr()
    local c_bisstring = package.loaded['mc.bitstring']
    local mock_bs = {
        new = function()
            return {
                pack = function()
                end,
                unpack = function()
                    return {
                        info_byte = 0
                    }
                end
            }
        end
    }

    package.loaded['mc.bitstring'] = nil
    package.loaded['mc.bitstring'] = mock_bs
    package.loaded['device.class.pcie_card'] = nil
    local mock_pcie_card = require ('device.class.pcie_card')

    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 = mock_pcie_card:pcie_get_capid_addr(pcie_info, 1)
    lu.assertEquals(ok, nil)

    ipmi.request = function ()
        return ipmi.types.Cc.Success
    end
    ok = mock_pcie_card:pcie_get_capid_addr(pcie_info, 1)
    lu.assertEquals(ok, nil)

    ipmi.request = c_request
    package.loaded['mc.bitstring'] = c_bisstring
end

function TestPcieCard:test_update_board_id()
    local pcie_addr_obj = {
        ['get_prop'] = function()
            return 'get'
        end,
        ['set_prop'] = function()
            return 'set'
        end
    }
    local pcie_card_obj = {
        ['set_prop'] = function()
            return 'set'
        end
    }
    local ret = c_pcie_card.update_vid_did(pcie_card_obj, pcie_addr_obj)
    lu.assertEquals(ret, nil)
    local ret = c_pcie_card.update_description(pcie_card_obj, "card")
    lu.assertEquals(ret, nil)
end

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

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

function TestPcieCard:test_set_sn_pn()
    local pcie_card_obj = {
        ['SerialNumber'] = '',
        ['PartNumber'] = '',
        ['board_sn'] = '',
        ['board_pn'] = '',
        ['pci_sn'] = '',
        ['pci_pn'] = '',
        ['set_prop'] = function(obj, prop, value)
            obj[prop] = value
        end,
        ['get_prop'] = function(obj, prop)
            return obj[prop]
        end,
        ['mds'] = {
            ObjectNmae = 'ObjectName'
        },
        ['set_sn_pn'] = function()
        end
    }

    pcie_card_obj['pci_sn'] = 'pci_sn'
    pcie_card_obj['pci_pn'] = 'pci_pn'
    local ok, _ = pcall(c_pcie_card.set_sn_pn, pcie_card_obj)
    lu.assertEquals(ok, true)
    lu.assertEquals(pcie_card_obj['pci_sn'], 'pci_sn')
    lu.assertEquals(pcie_card_obj['pci_pn'], 'pci_pn')

    pcie_card_obj['board_sn'] = 'board_sn'
    pcie_card_obj['board_pn'] = 'board_pn'
    ok, _ = pcall(c_pcie_card.set_sn_pn, pcie_card_obj)
    lu.assertEquals(ok, true)
    lu.assertEquals(pcie_card_obj['board_sn'], 'board_sn')
    lu.assertEquals(pcie_card_obj['board_pn'], 'board_pn')

    ok, _ = pcall(c_pcie_card.set_board_sn_pn, pcie_card_obj, '1', '2')
    lu.assertEquals(ok, true)
    lu.assertEquals(pcie_card_obj['board_sn'], '1')
    lu.assertEquals(pcie_card_obj['board_pn'], '2')
end

function TestPcieCard:test_listen_board_sn_pn()
    local bak_GetBoardObjects = client.GetBoardObjects
    local bak_OnBoardInterfacesAdded = client.OnBoardInterfacesAdded
    local bak_OnBoardPropertiesChanged = client.OnBoardPropertiesChanged
    local bak_ForeachBoardObjects = client.ForeachBoardObjects
    client.GetBoardObjects = function()
        return {
            ['path'] = {
                extra_params = {
                    Position = 'Position'
                },
                BoardSerialNumber = 'BoardSerialNumber',
                BoardPartNumber = 'BoardPartNumber'
            }
        }
    end

    client.OnBoardInterfacesAdded = function(_self, cb)
        local props = {
            BoardSerialNumber = {
                value = function()
                    return 'BoardSerialNumber'
                end
            },
            BoardPartNumber = {
                value = function()
                    return 'BoardPartNumber'
                end
            }
        }
        cb('_', 'path', props)
    end
    client.OnBoardPropertiesChanged = function(_self, cb)
        cb('_', 'path', '_')
    end
    client.ForeachBoardObjects = function(_self, cb)
        cb({
            extra_params = {
                Position = 'Position'
            },
            BoardSerialNumber = 'BoardSerialNumber',
            BoardPartNumber = 'BoardPartNumber'
        })
    end

    local pcie_card_obj = {
        ['position'] = 'Position',
        ['mds'] = {
            ObjectName = 'ObjectName'
        },
        ['SerialNumber'] = '',
        ['PartNumber'] = '',
        ['board_sn'] = '',
        ['board_pn'] = '',
        ['pci_sn'] = '',
        ['pci_pn'] = '',
        ['set_prop'] = function(obj, prop, value)
            obj[prop] = value
        end,
        ['get_prop'] = function(obj, prop)
            return obj[prop]
        end,
        ['set_board_sn_pn'] = function(self, sn, pn)
            self.board_sn = sn
            self.board_pn = pn
        end
    }
    local ok, _ = pcall(c_pcie_card.listen_board_sn_pn, pcie_card_obj)
    lu.assertEquals(ok, true)
    lu.assertEquals(pcie_card_obj['board_sn'], 'BoardSerialNumber')
    lu.assertEquals(pcie_card_obj['board_pn'], 'BoardPartNumber')

    client.GetBoardObjects = bak_GetBoardObjects
    client.OnBoardInterfacesAdded = bak_OnBoardInterfacesAdded
    client.OnBoardPropertiesChanged = bak_OnBoardPropertiesChanged
    client.ForeachBoardObjects = bak_ForeachBoardObjects
end

function TestPcieCard:test_update_pcie_info()
    local pcie_device_obj = {
        get_prop = function(self, prop_name)
            return 0
        end
    }
    local pcie_info_from_imu = {
        VendorID = 1,
        DeviceID = 1,
        SubVendorID = 1,
        SubDeviceID = 1
    }
    c_pcie_card.pcie_info = {}
    c_pcie_card:update_pcie_info(pcie_device_obj, pcie_info_from_imu)
end