-- 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 device_mgmt = require 'device_mgmt.device_mgmt'
local pcie_card_mgmt = require 'device_mgmt.pcie_card_mgmt'
local log = require 'mc.logging'
local org_freedesktop_dbus = require 'sd_bus.org_freedesktop_dbus'
local match_rule = org_freedesktop_dbus.MatchRule
local mdb = require 'mc.mdb'
local c_device_service = require 'device.device_service'
local cmn = require 'common'
local comm_fun = require 'device_mgmt.comm_fun'

TestPCIeCardMgmt = {}

local mds_obj = {
}

function TestPCIeCardMgmt:setupClass()
end

function TestPCIeCardMgmt:teardownClass()
end

function TestPCIeCardMgmt:test_device_mgmt()
    local pcie_card_mgmt_bak = pcie_card_mgmt
    pcie_card_mgmt.new = function()
        log:raise("pcie_card_mgmt init error")
    end
    local ok, ret = pcall(function()
        local device_mgmt = device_mgmt.new({})
    end)
    lu.assertEquals(ok, true)
    lu.assertEquals(ret, nil)

    pcie_card_mgmt = pcie_card_mgmt_bak
end

function TestPCIeCardMgmt:test_sync_dev_prop_to_resource_obj()
    local resource_obj = {}
    local interface = 'bmc.dev.PCIeDevice'
    local prop = 'VendorId'
    local value = '1'
    pcall(pcie_card_mgmt.sync_dev_prop_to_resource_obj, pcie_card_mgmt, resource_obj, interface, prop, value)
    prop = 'DeviceId'
    value = '2'
    pcall(pcie_card_mgmt.sync_dev_prop_to_resource_obj, pcie_card_mgmt, resource_obj, interface, prop, value)
    prop = 'SubSystemVendorId'
    value = '3'
    pcall(pcie_card_mgmt.sync_dev_prop_to_resource_obj, pcie_card_mgmt, resource_obj, interface, prop, value)
    prop = 'SubSystemDeviceId'
    value = '4'
    pcall(pcie_card_mgmt.sync_dev_prop_to_resource_obj, pcie_card_mgmt, resource_obj, interface, prop, value)
    lu.assertEquals(resource_obj.VendorID, 1)
    lu.assertEquals(resource_obj.DeviceID, 2)
    lu.assertEquals(resource_obj.SubVendorID, 3)
    lu.assertEquals(resource_obj.SubDeviceID, 4)

    prop = 'VendorId'
    value = '-1'
    pcall(pcie_card_mgmt.sync_dev_prop_to_resource_obj, pcie_card_mgmt, resource_obj, interface, prop, value)
    prop = 'DeviceId'
    value = '65536'
    pcall(pcie_card_mgmt.sync_dev_prop_to_resource_obj, pcie_card_mgmt, resource_obj, interface, prop, value)
    prop = 'SubSystemVendorId'
    value = ''
    pcall(pcie_card_mgmt.sync_dev_prop_to_resource_obj, pcie_card_mgmt, resource_obj, interface, prop, value)
    prop = 'SubSystemDeviceId'
    value = '65535'
    pcall(pcie_card_mgmt.sync_dev_prop_to_resource_obj, pcie_card_mgmt, resource_obj, interface, prop, value)
    lu.assertEquals(resource_obj.VendorID, 65535)
    lu.assertEquals(resource_obj.DeviceID, 65535)
    lu.assertEquals(resource_obj.SubVendorID, 65535)
    lu.assertEquals(resource_obj.SubDeviceID, 65535)

    interface = 'bmc.dev.PCIeCard'
    prop = 'BoardId'
    value = math.nan        -- 无效值
    pcall(pcie_card_mgmt.sync_dev_prop_to_resource_obj, pcie_card_mgmt, resource_obj, interface, prop, value)
    lu.assertEquals(resource_obj.BoardId, nil)
    prop = 'PcbId'
    value = 1
    pcall(pcie_card_mgmt.sync_dev_prop_to_resource_obj, pcie_card_mgmt, resource_obj, interface, prop, value)
    lu.assertEquals(resource_obj.PcbID, nil)
    resource_obj.PcbID = 2
    pcall(pcie_card_mgmt.sync_dev_prop_to_resource_obj, pcie_card_mgmt, resource_obj, interface, prop, value)
    lu.assertEquals(resource_obj.PcbID, 1)
    
    prop = 'PcbVersion'
    value = 1
    pcall(pcie_card_mgmt.sync_dev_prop_to_resource_obj, pcie_card_mgmt, resource_obj, interface, prop, value)
    lu.assertEquals(resource_obj.PcbVersion, nil)
    resource_obj.PcbVersion = 2
    pcall(pcie_card_mgmt.sync_dev_prop_to_resource_obj, pcie_card_mgmt, resource_obj, interface, prop, value)
    lu.assertEquals(resource_obj.PcbVersion, 1)
end

function TestPCIeCardMgmt:test_listen_device_obj_property_change()
    local resource_obj = {
        BoardID = 65535,
        BoardName = ""
    }
    local msg = {
        read = function()
            return 'bmc.dev.PCIeCard', {
                Name = {
                    value = function()
                        return 'SP686'
                    end
                },
                BoardId = {
                    value = function()
                        return 1
                    end
                },
                Description = {
                    value = function()
                        return 'NA'
                    end
                }
            }
        end
    }
    local bus = {
        match = function(self, property_changed_sig, cb)
            cb(msg)
            return 'sig'
        end
    }
    local c_signal = match_rule.signal
    match_rule.signal = function()
        return {
            with_path = function() end
        }
    end
    local sig_slot = {}
    local ok = pcall(pcie_card_mgmt.listen_device_obj_property_change, pcie_card_mgmt, bus, 
                    sig_slot, nil, resource_obj)
    match_rule.signal = c_signal
    lu.assertEquals(ok, true)
    lu.assertEquals(sig_slot[1], 'sig')
    lu.assertEquals(resource_obj.Name, 'SP686')
    lu.assertEquals(resource_obj.BoardID, 1)
    lu.assertEquals(resource_obj.Description, 'NA')
end

function TestPCIeCardMgmt:test_synchronize_property()
    local resource_obj = {
        BoardID = 65535
    }
    local bus = {
        call = function(obj, service)
            if service == 'bmc.kepler.maca' then
                return {
                    test_service = {
                        'bmc.dev.PCIeCard'
                    }
                }
            end
            if service == 'test_service' then
                return {
                    BoardId = 0
                }
            end 
        end
    }
    local c_register_interface = mdb.register_interface
    mdb.register_interface = function()
        return true
    end
    local c_get_object = mdb.get_object
    mdb.get_object = function()
        return {
            get_property = function()
                return 0, 1
            end
        }
    end
    local ok = pcall(pcie_card_mgmt.synchronize_property, pcie_card_mgmt, bus, nil, resource_obj)
    mdb.register_interface = c_register_interface
    mdb.get_object = c_get_object
    lu.assertEquals(ok, true)
    lu.assertEquals(resource_obj.BoardID, 1)
end

function TestPCIeCardMgmt:test_create_resource_obj()
    local bus = {
        call = function()
            return {
                {
                    'bmc.kepler.maca',
                    'bmc.kepler.pcie_device'
                }
            }
        end
    }
    local c_register_interface = mdb.register_interface
    mdb.register_interface = function()
        return true
    end
    local c_get_object = mdb.get_object
    mdb.get_object = function()
        return {
            get_property = function()
                return 0, 'PCIeCard_1'
            end
        }
    end
    local obj = {}
    local c_app = pcie_card_mgmt.app
    pcie_card_mgmt.app = {
        CreatePCIeCard = function(self, num, obj_name, cb)
            cb(obj)
            return true
        end,
        create_fun = {
            ["PCIeCard"] = function(self, num, obj_name, cb)
                cb(obj)
                return true
            end,
            ["OCPCard"] = function(self, num, obj_name, cb)
                cb(obj)
                return true
            end
        }
    }
    local ok, _, _ = pcall(pcie_card_mgmt.create_resource_obj, pcie_card_mgmt, bus, "dev_path", "PCIeCard")
    if not ok then
        return
    end
    mdb.register_interface = c_register_interface
    mdb.get_object = c_get_object
    pcie_card_mgmt.app = c_app
    lu.assertEquals(ok, true)
    lu.assertEquals(obj.ObjectName, nil)
end

function TestPCIeCardMgmt:test_init_obj()
    local c_objects = pcie_card_mgmt.objects
    pcie_card_mgmt.objects = {
        ['/opt/bmc/pcie_card'] = 0
    }
    local c_create_resource_obj = pcie_card_mgmt.create_resource_obj
    pcie_card_mgmt.create_resource_obj = function()
        return true
    end
    local c_listen_device_obj_property_change = pcie_card_mgmt.listen_device_obj_property_change
    pcie_card_mgmt.listen_device_obj_property_change = function()
        return true
    end
    local c_synchronize_property = pcie_card_mgmt.synchronize_property
    pcie_card_mgmt.synchronize_property = function()
        return true
    end
    local c_get_instance = c_device_service.get_instance
    c_device_service.get_instance = function()
        return {
            on_add_object = function()
                return true
            end
        }
    end
    local ok = pcall(pcie_card_mgmt.init_obj, pcie_card_mgmt, '/opt/bmc/pcie_card')
    lu.assertEquals(ok, true)
    ok = pcall(pcie_card_mgmt.init_obj, pcie_card_mgmt, '/opt/bmc')
    lu.assertEquals(ok, true)
    pcie_card_mgmt.objects = c_objects
    pcie_card_mgmt.create_resource_obj = c_create_resource_obj
    pcie_card_mgmt.listen_device_obj_property_change = c_listen_device_obj_property_change
    pcie_card_mgmt.synchronize_property = c_synchronize_property
    c_device_service.get_instance = c_get_instance
end

function TestPCIeCardMgmt:test_get_pcie_device_prop_to_card_retry()
    cmn.skynet.fork = function(cb)
        cb()
    end
    cmn.skynet.sleep = function()
    end
    local pcie_card_obj = {
        ['ObjectName'] = 'Gpu_1_0101010101'
    }
    local c_get_instance = c_device_service.get_instance
    c_device_service.get_instance = function()
        return {
            pcie_device_list = {
                [1] = {
                    get_prop = function(item, prop)
                        if prop == 'ObjectName' then
                            return 'Gpu_1_0101010101'
                        end
                        if prop == 'SlotID' then
                            return 1
                        end
                        if prop == 'DevBus' then
                            return 22
                        end
                        if prop == 'DevDevice' then
                            return 22
                        end
                        if prop == 'DevFunction' then
                            return 22
                        end
                        return 'test'
                    end
                }            
            }
        }
    end
    local ok = pcall(pcie_card_mgmt.get_pcie_device_prop_to_card_retry, pcie_card_mgmt, pcie_card_obj)
    lu.assertEquals(ok, true)
    lu.assertEquals(pcie_card_obj['DevBus'], nil)
    pcie_card_obj = {
        ['ObjectName'] = 'Gpu_1_0101010101',
        ['DevBus'] = 1,
        ['DevDevice'] = 1,
        ['DevFunction'] = 1
    }
    ok = pcall(pcie_card_mgmt.get_pcie_device_prop_to_card_retry, pcie_card_mgmt, pcie_card_obj)
    lu.assertEquals(ok, true)
    lu.assertEquals(pcie_card_obj['DevBus'], 22)
    c_device_service.get_instance = c_get_instance
end

function TestPCIeCardMgmt:test_on_del_device_obj()
    local del_signal_bak = comm_fun.set_interface_del_signal
    pcie_card_mgmt.objects = {}
    pcie_card_mgmt.create_fun = {}
    comm_fun.set_interface_del_signal = function(_, _, _, _, cb)
        cb('device_path')
    end
    pcie_card_mgmt:on_del_device_obj()
    pcie_card_mgmt.objects = {
        device_path = {
            ObjectIdentifier = {1,2,3,4},
            ObjectName = 'ObjectName',
            ClassName = ''
        }
    }
    pcie_card_mgmt:on_del_device_obj()
    lu.assertEquals(#pcie_card_mgmt.objects['device_path'], 0)
    comm_fun.set_interface_del_signal = del_signal_bak
end

function TestPCIeCardMgmt:test_sync_resource_prop_to_dev_obj()
    local dev_obj = {
        PartNumber = "",
        SerialNumber = ""
    }

    pcie_card_mgmt:sync_resource_prop_to_dev_obj(dev_obj, "PartNumber", "0x0101")
    pcie_card_mgmt:sync_resource_prop_to_dev_obj(dev_obj, "SerialNumber", "0x0101")

    lu.assertEquals(dev_obj.PartNumber, "0x0101")
    lu.assertEquals(dev_obj.SerialNumber, "0x0101")

    pcall(function()
        pcie_card_mgmt:sync_resource_prop_to_dev_obj(nil, "PartNumber", "0x0101")
    end)
end

function TestPCIeCardMgmt:test_pcall_sync_resource_prop_to_dev_obj()
    local tmp_dev_obj = pcie_card_mgmt.sync_resource_prop_to_dev_obj
    local flag = 0
    pcie_card_mgmt.sync_resource_prop_to_dev_obj = function(...)
        flag = 1
    end

    pcie_card_mgmt:pcall_sync_resource_prop_to_dev_obj()
    lu.assertEquals(flag, 1)

    pcie_card_mgmt.sync_resource_prop_to_dev_obj = function(...)
        error("test error", 1)
        flag = 2
    end

    pcie_card_mgmt:pcall_sync_resource_prop_to_dev_obj()
    lu.assertEquals(flag, 1)
    pcie_card_mgmt.sync_resource_prop_to_dev_obj = tmp_dev_obj
end

function TestPCIeCardMgmt:test_set_boardid_and_boardname()
    local resource_obj = {
        BoardID = 65535,
        BoardName = ""
    }
    pcie_card_mgmt:sync_dev_prop_to_resource_obj(resource_obj, "bmc.dev.PCIeCard", "BoardId", 65535)
    lu.assertEquals(resource_obj.BoardID, 65535)
    resource_obj.BoardID = 1
    pcie_card_mgmt:sync_dev_prop_to_resource_obj(resource_obj, "bmc.dev.PCIeCard", "BoardId", 65535)
    lu.assertEquals(resource_obj.BoardID, 1)
    pcie_card_mgmt:sync_dev_prop_to_resource_obj(resource_obj, "bmc.dev.PCIeCard", "BoardName", "Name")
    lu.assertEquals(resource_obj.BoardName, "Name")
    pcie_card_mgmt:sync_dev_prop_to_resource_obj(resource_obj, "bmc.dev.PCIeCard", "BoardName", "UnName")
    lu.assertEquals(resource_obj.BoardName, "Name")

    pcie_card_mgmt:sync_dev_prop_to_resource_obj(resource_obj, "bmc.dev.Board", "Id", nil)
    lu.assertEquals(resource_obj.BoardID, 1)
    pcie_card_mgmt:sync_dev_prop_to_resource_obj(resource_obj, "bmc.dev.Board", "Id", 2)
    lu.assertEquals(resource_obj.BoardID, 2)
    pcie_card_mgmt:sync_dev_prop_to_resource_obj(resource_obj, "bmc.dev.Board", "Name", nil)
    lu.assertEquals(resource_obj.BoardName, "Name")
    pcie_card_mgmt:sync_dev_prop_to_resource_obj(resource_obj, "bmc.dev.Board", "Name", "UnName")
    lu.assertEquals(resource_obj.BoardName, "UnName")
end