-- 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_device_mgmt = require 'device_mgmt.pcie_device_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 comm_fun = require 'device_mgmt.comm_fun'
local cmn = require 'common'
local clsmgmt = require 'mc.class_mgnt'

TestPCIeDeviceMgmt = {}

local mds_obj = {
}

function TestPCIeDeviceMgmt:setupClass()
end

function TestPCIeDeviceMgmt:teardownClass()
end

function TestPCIeDeviceMgmt:test_device_mgmt()
    local pcie_device_mgmt_bak = pcie_device_mgmt
    pcie_device_mgmt.new = function()
        log:raise("gpu init error")
    end
    local ok, ret = pcall(function()
        local device_mgmt = device_mgmt.new({})
    end)
    lu.assertEquals(ok, true)
    lu.assertEquals(ret, nil)

    pcie_device_mgmt = pcie_device_mgmt_bak
end

function TestPCIeDeviceMgmt:test_listen_device_obj_property_change()
    local resource_obj = {}
    local msg = {
        read = function()
            return 'bmc.dev.PCIeDevice', {
                DeviceName = {
                    value = function()
                        return 'SP686'
                    end
                },
                FunctionClass = {
                    value = function()
                        return 1
                    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 = {}
    pcie_device_mgmt.dev_obj_device_name_init = {}
    local ok = pcall(pcie_device_mgmt.listen_device_obj_property_change, pcie_device_mgmt, bus, 
                    sig_slot, 'device_path', resource_obj)
    match_rule.signal = c_signal
    lu.assertEquals(ok, true)
    lu.assertEquals(sig_slot[1], 'sig')
    lu.assertEquals(resource_obj.DeviceName, 'SP686')
    lu.assertEquals(resource_obj.FunctionClass, 1)
end

function TestPCIeDeviceMgmt:test_synchronize_property()
    local resource_obj = {}
    local bus = {
        call = function(obj, service)
            if service == 'bmc.kepler.maca' then
                return {
                    test_service = {
                        'bmc.dev.PCIeDevice'
                    }
                }
            end
            if service == 'test_service' then
                return {
                    FunctionClass = 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_device_mgmt.synchronize_property, pcie_device_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.FunctionClass, 1)
end

function TestPCIeDeviceMgmt: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, 'PCIeDevie_1'
            end
        }
    end
    local obj = {}
    local c_app = pcie_device_mgmt.app
    pcie_device_mgmt.app = {
        CreatePCIeDevice = function(self, num, obj_name, cb)
            cb(obj)
            return obj
        end
    }
    local ok, _, _ = pcall(pcie_device_mgmt.create_resource_obj, pcie_device_mgmt, bus)
    mdb.register_interface = c_register_interface
    mdb.get_object = c_get_object
    pcie_device_mgmt.app = c_app
    lu.assertEquals(ok, true)
    lu.assertEquals(obj.ObjectName, 'PCIeDevice_1')
end

function TestPCIeDeviceMgmt:test_init_obj()
    local c_objects = pcie_device_mgmt.objects
    pcie_device_mgmt.objects = {
        ['/opt/bmc/pcie_device'] = 0
    }
    local c_get_device_obj_by_path = comm_fun.get_device_obj_by_path
    comm_fun.get_device_obj_by_path = function()
        return {
            get_property = function()
                return 0, "pcie_device"
            end
        }
    end
    cmn.skynet.sleep = function()
    end
    local c_dev_obj_name_paths = pcie_device_mgmt.dev_obj_name_paths
    pcie_device_mgmt.dev_obj_name_paths = {}
    pcie_device_mgmt.dev_obj_set_dev_bus_done = {}
    pcie_device_mgmt.dev_obj_device_name_init = {}
    local c_create_resource_obj = pcie_device_mgmt.create_resource_obj
    pcie_device_mgmt.create_resource_obj = function()
        return true
    end
    local c_listen_device_obj_property_change = pcie_device_mgmt.listen_device_obj_property_change
    pcie_device_mgmt.listen_device_obj_property_change = function()
        return true
    end
    local c_synchronize_property = pcie_device_mgmt.synchronize_property
    pcie_device_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,
            on_add_object_complete = function()
                return true
            end
        }
    end
    local ok = pcall(pcie_device_mgmt.init_obj, pcie_device_mgmt, '/opt/bmc/pcie_device')
    lu.assertEquals(ok, true)
    ok = pcall(pcie_device_mgmt.init_obj, pcie_device_mgmt, '/opt/bmc')
    lu.assertEquals(ok, true)
    pcie_device_mgmt.objects = c_objects
    comm_fun.get_device_obj_by_path = c_get_device_obj_by_path
    pcie_device_mgmt.dev_obj_name_paths = c_dev_obj_name_paths
    pcie_device_mgmt.dev_obj_set_dev_bus_done = {}
    pcie_device_mgmt.dev_obj_device_name_init = {} 
    pcie_device_mgmt.create_resource_obj = c_create_resource_obj
    pcie_device_mgmt.listen_device_obj_property_change = c_listen_device_obj_property_change
    pcie_device_mgmt.synchronize_property = c_synchronize_property
    c_device_service.get_instance = c_get_instance
end

function TestPCIeDeviceMgmt:test_sync_all_resource_prop_to_dev_obj()
    local sync_bak = pcie_device_mgmt.set_device_bdf
    local c_get_device_obj_by_path = comm_fun.get_device_obj_by_path
    local resource_obj = {
        ["SlotID"] = 1,
        ["DeviceName"] = 'device name'
    }
    local dev_obj = {}
    comm_fun.get_device_obj_by_path = function()
        return dev_obj
    end
    pcie_device_mgmt:sync_all_resource_prop_to_dev_obj(resource_obj, dev_path)
    lu.assertEquals(dev_obj["DeviceName"], 'device name')
    pcie_device_mgmt.set_device_bdf = function()
        error('break')
    end
    pcie_device_mgmt:sync_all_resource_prop_to_dev_obj(resource_obj, dev_path)
    lu.assertEquals(dev_obj["DeviceName"], 'device name')
    pcie_device_mgmt.set_device_bdf = sync_bak
    comm_fun.get_device_obj_by_path = c_get_device_obj_by_path
end

function TestPCIeDeviceMgmt:test_set_device_bdf_init()
    local test_obj = {
        dev_obj_set_dev_bus_done = {
            ['/bmc/dev/Systems/1/Gpu_1_0101010101'] = false
        },
        objects = {
            ['/bmc/dev/Systems/1/Gpu_1_0101010101'] = {
                ["DevBus"] = 22
            }
        }
    }
    local c_get_device_obj_by_path = comm_fun.get_device_obj_by_path
    comm_fun.get_device_obj_by_path = function()
        return {
            call_method = function()
                return false, false
            end
        }
    end
    test_obj.set_device_bdf = function()
    end
    pcie_device_mgmt.set_device_bdf_init(test_obj)
    lu.assertEquals(test_obj.dev_obj_set_dev_bus_done['/bmc/dev/Systems/1/Gpu_1_0101010101'], false)
    
    comm_fun.get_device_obj_by_path = c_get_device_obj_by_path
end

function TestPCIeDeviceMgmt:test_set_device_bdf()
    local test_obj = {
        dev_obj_set_dev_bus_done = {
            ['/bmc/dev/Systems/1/Gpu_1_0101010101'] = false
        },
        objects = {
            ['/bmc/dev/Systems/1/Gpu_1_0101010101'] = {
                ["DevBus"] = 22
            }
        }
    }
    local c_get_device_obj_by_path = comm_fun.get_device_obj_by_path
    local dev_path = '/bmc/dev/Systems/1/Gpu_1_0101010101'
    local device_obj = {
        call_method = function()
            return false, false
        end
    }
    local prop = "DevBus"
    local value = 22
    pcie_device_mgmt.set_device_bdf(test_obj, dev_path, device_obj, prop, value)
    lu.assertEquals(test_obj.dev_obj_set_dev_bus_done['/bmc/dev/Systems/1/Gpu_1_0101010101'], false)

    device_obj = {
        call_method = function()
            return true, true
        end
    }
    pcie_device_mgmt.set_device_bdf(test_obj, dev_path, device_obj, prop, value)
    lu.assertEquals(test_obj.dev_obj_set_dev_bus_done['/bmc/dev/Systems/1/Gpu_1_0101010101'], true)
    
    comm_fun.get_device_obj_by_path = c_get_device_obj_by_path
end

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

function TestPCIeDeviceMgmt:test_sync_resource_prop_to_dev_obj()
    local dev_obj = {}
    local prop = 'SlotID'
    local value = '1'
    pcall(pcie_device_mgmt.sync_resource_prop_to_dev_obj, pcie_device_mgmt, dev_obj, prop, value)
    lu.assertEquals(dev_obj.Slot, '1')

    prop = 'Position'
    value = '1'
    pcall(pcie_device_mgmt.sync_resource_prop_to_dev_obj, pcie_device_mgmt, dev_obj, prop, value)
    lu.assertEquals(dev_obj.Location, '1')
end