-- 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_network_port = require 'device.class.network_port'
local c_object_manage = require 'mc.orm.object_manage'
local npu_imu_cmd = require 'npu.hdk_cmd'
local test_utils = require 'test_utils'
local class_mgnt = require "mc.class_mgnt"
local fructl = require 'infrastructure.fructl'
skynet = require 'skynet'
local c_network_port = require 'device.class.network_port'

TEST_network_port = {}

local INVALID_DATA_STRING<const> = 'N/A'

local mock_imu_info = {
    received = 'test',
    transmitted = 'test_transmitted',
    dropped = 'test_dropped',
    mac_addr = 'test_mac_addr',
    rx_fcs_err_pkt_num = 'test_rx_fcs_err_pkt_num',
    subnet_mask = 'test_subnet_mask',
    gateway = 'test_gateway',
    ip_addr = 'test_ipaddr'
}

local function mock_get_info_from_imu(id, power_on)
    return mock_imu_info
end

local function mock_create_ipv4_addr(Object, SystemID, ID1, ID2, ID3, prop_setting_cb)
    local path = '/bmc/kepler/Systems/' .. SystemID .. '/NetworkAdapters/' .. ID1 .. '/Ports/' ..
                ID2 .. '/IPv4Addresses/' .. ID3 .. ''
    local obj = {path = path}
    prop_setting_cb(obj)
    return obj
end

local function mock_get_instance()
    return {
        app = {
            CreateIPv4Address = mock_create_ipv4_addr
        } 
    }
end

local function mock_get_postion()
    return 'test_postion'
end

local function mock_class_mgnt(...)
    return {
        remove = function(...)
            return nil
        end
    }
end

local function mock_get_power_status(...)
    return 'OFF'
end

local mock_network_port = {
    PortID = 1,
    RXFrames = 'test',
    TXFrames = 'test',
    PacketsDropped = 'test',
    MACAddress = 'test',
    RXFCSErrors = 'test',
    npu_ipv4_obj = false,
    npu_ipv4_info = {
        Address = INVALID_DATA_STRING,
        SubnetMask = INVALID_DATA_STRING,
        Gateway = INVALID_DATA_STRING
    },
    get_position = mock_get_postion,
    clear_npu_port_basic_info = function(...)
        return nil
    end,
    get_npu_port_basic_info = function()
        return 'str' + 1
    end,
    sleep_ms = function(...)
        
    end
}

local get_info_from_imu = npu_imu_cmd.get_info_from_imu
local get_instance = c_object_manage.get_instance
local get_power_status = fructl.get_power_status

function TEST_network_port:setUp()
    npu_imu_cmd.get_info_from_imu = mock_get_info_from_imu
    c_object_manage.get_instance = mock_get_instance
    fructl.get_power_status = mock_get_power_status
end

function TEST_network_port:tearDown()
    npu_imu_cmd.get_info_from_imu = get_info_from_imu
    c_object_manage.get_instance = get_instance
    fructl.get_power_status = get_power_status
end

local test_data = {
    LinkStatus = 'N/A',
    NetworkAdapterId = 0,
    PortID = 0,
    SpeedMbps = 0,
    AutoSpeedNegotiation = false,
    FullDuplex = false
}
-- 测试更新链接状态
function TEST_network_port:test_update_ncsi_link_status()
    c_network_port.t_port_prop_without_bma = {}
    c_network_port.schedulers = {}
    c_network_port.ncsi_config_obj = {
        LinkStatus = function()
            return {
                on_data_change = {
                    on = function(self, func)
                        func(test_data)
                        c_network_port.ncsi_get_linkstatus_retry_count = 2
                    end
                },
                on_error = {
                    on = function(self, func)
                        func()
                    end
                },
                start = function()
                end
            }
        end
    }
    c_network_port:update_ncsi_link_status()
    lu.assertIsFalse(c_network_port.AutoSpeedNegotiation)
    lu.assertIsFalse(c_network_port.FullDuplex)
    lu.assertEquals(c_network_port.LinkStatus, test_data.LinkStatus)
    lu.assertEquals(c_network_port.LinkStatus, test_data.LinkStatus)
    lu.assertEquals(c_network_port.SpeedMbps, test_data.SpeedMbps)
end

function TEST_network_port:test_get_npu_port_basic_info()
    mock_network_port.npu_ipv4_obj = true
    mock_imu_info.ip_addr = INVALID_DATA_STRING
    mock_imu_info.subnet_mask = INVALID_DATA_STRING
    mock_imu_info.gateway = INVALID_DATA_STRING

    -- 测试npu仅能获取一个ipv4信息，当ip网关掩码均不变时不做资源树对象操作场景
    c_network_port.get_npu_port_basic_info(mock_network_port)
    lu.assertEquals(mock_network_port.npu_ipv4_info.Address, INVALID_DATA_STRING)

    -- 测试当ip网关掩码均为无效值时不上树场景
    mock_network_port.npu_ipv4_obj = false
    c_network_port.get_npu_port_basic_info(mock_network_port)
    lu.assertEquals(mock_network_port.npu_ipv4_info.Address, INVALID_DATA_STRING)

    -- 测试更新ipv4缓存信息并上树场景
    mock_imu_info.ip_addr = 'test_ip_addr_xx'
    mock_imu_info.subnet_mask = 'test_ip_addr'
    mock_imu_info.gateway = 'test_ip_addr'
    c_network_port.get_npu_port_basic_info(mock_network_port)
    lu.assertEquals(mock_network_port.npu_ipv4_obj.Address, 'test_ip_addr_xx')
end

function TEST_network_port:test_clear_npu_port_basic_info()
    mock_network_port.npu_ipv4_obj = {
        IPv4Address = 'test'
    }
    pcall(function ()
            c_network_port.clear_npu_port_basic_info(mock_network_port)
        end
    )
    lu.assertEquals(mock_network_port.npu_ipv4_info.Address, INVALID_DATA_STRING)
end

function TEST_network_port:test_get_npu_port_info()
    c_network_port.get_npu_port_info(mock_network_port)
end

function TEST_network_port:test_get_info_from_imu()
    c_network_port.get_info_from_imu(mock_network_port)
end

function TEST_network_port:test_get_npu_cdr_temp_from_imu_fail()
    c_network_port.is_npu_heartbeat_loss = true
    c_network_port.get_npu_cdr_temp_from_imu(mock_network_port)
end

function TEST_network_port:test_get_npu_cdr_temp_from_imu()
    c_network_port.is_npu_heartbeat_loss = false
    c_network_port.get_npu_cdr_temp_from_imu(mock_network_port)
end

function TEST_network_port:test_mctp()
    c_network_port.schedulers = {}
    c_network_port.ncsi_config_obj = {
        GetLLDPCapability = function()
            return {
                on_data_change = { on = function(data) end },
                start = function () end
            }
        end,
        PortMetrics = function()
            return {
                on_data_change = { on = function(data) end },
                start = function () end
            }
        end,
        LinkStatus = function()
            return {
                on_data_change = { on = function(data) end },
                on_error = { on = function(data) end },
                start = function () end
            }
        end,
        InitializeNCSIChannel =  function() return { value = function() return {} end } end,
        BDF = function()
            return { value = function() return { bus = 1, device = 1, func = 1 } end }
        end,
        MacAddrNCSI =  function() return { value = function() return {} end } end,
        DefaultMacAddrNCSI =  function() return { value = function() return {} end } end,
        DCBX =  function() return { value = function() return {} end } end,
        SetOnLLDPCapability =  function() return { value = function() return {} end } end,
        SetOffLLDPCapability = function() return { value = function() return {} end } end,
        ResetNic =  function() return { value = function() return {} end } end
    }
    c_network_port:update_LLDP_enabled_by_ncsi()
    c_network_port:update_ncsi_port_stats()
    c_network_port:update_ncsi_link_status()
    c_network_port:initialize_ncsi_channel()
    c_network_port:update_BDF_by_ncsi()
    c_network_port:update_DCBX_by_ncsi()
    c_network_port:set_package_id(1)
    c_network_port:set_LLDP_capability(true)
    c_network_port:set_LLDP_capability(false)
end

local mockup_func = {}
local function mockup_func_backup()
    mockup_func.connect_signal = c_network_port.connect_signal
    mockup_func.get_power_status = fructl.get_power_status
    mockup_func.get_system_reset_flag = fructl.get_system_reset_flag
end
local function mockup_function()
    c_network_port.connect_signal = function(self, on_cb, cb)
        cb(self.name, self.value)
    end
    fructl.get_power_status = function()
        return "ON"
    end
end
local function mockup_func_restore()
    c_network_port.connect_signal = mockup_func.connect_signal
    fructl.get_power_status = mockup_func.get_power_status
    fructl.get_system_reset_flag = mockup_func.get_system_reset_flag
end

function TEST_network_port:test_start_npu_link_status_listening()
    mockup_func_backup()
    mockup_function()
    fructl.get_system_reset_flag = function()
        return 2
    end
    c_network_port.value = 0
    c_network_port.name = "LinkStatusValue"
    c_network_port.LinkStatusNumeric = 255
    c_network_port:start_npu_link_status_listening()
    lu.assertEquals(c_network_port.LinkStatusNumeric, 2)

    c_network_port.LinkStatusNumeric = 1
    c_network_port:start_npu_link_status_listening()
    lu.assertEquals(c_network_port.LinkStatusNumeric, 0)

    c_network_port.value = 1
    c_network_port:start_npu_link_status_listening()
    lu.assertEquals(c_network_port.LinkStatusNumeric, 1)

    mockup_func_restore()
end

function TEST_network_port:test_register_npu_changed_callback()
    mockup_func_backup()
    mockup_function()
    local start_npu_link_status_listening = c_network_port.start_npu_link_status_listening
    c_network_port.start_npu_link_status_listening = function()
    end
    fructl.get_system_reset_flag = function()
        return 0
    end
    c_network_port.LinkStatusValue = 1
    c_network_port:register_npu_changed_callback()
    lu.assertEquals(c_network_port.LinkStatusNumeric, 1)

    c_network_port.LinkStatusValue = 2
    c_network_port:register_npu_changed_callback()
    lu.assertEquals(c_network_port.LinkStatusNumeric, 2)

    c_network_port.start_npu_link_status_listening = start_npu_link_status_listening
    mockup_func_restore()
end