-- Copyright (c) 2025 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 hypercard_obj = require 'dpu_service.hypercard_object'
local hypercard_smbus_cfg = require 'protocol.smbus_hypercard'
local add_event = require 'add_event'
TestHypercard = {}

local bmc_chip_data = {
    '\x80', '\x0b', '\x1f', '\x53', '\x01', '\xff', '\x20', '\x00', '\xf0', '\x84',
    '\x30', '\xce', '\x02', '\x23', '\x5c', '\x00', '\x00', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x03', '\x03', '\x03', '\xff', '\xff', '\xff', '\xff',
    '\xff', '\xff', '\x6e', '\x76', '\x86', '\x52', '\x0c', '\x49', '\x49', '\x49',
    '\xff', '\xff', '\xff', '\xff', '\x5a', '\x31', '\xff', '\xff', '\x2d', '\x2b',
    '\x39', '\xfe', '\xfe', '\xfe', '\xfe', '\xfe', '\xfe', '\x00', '\xff', '\xff',
    '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
    '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
    '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
    '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xfe', '\xff', '\xff'
}

local cpld_chip_data = {
    '\x8a', '\x01', '\x1f', '\x53', '\x01', '\x51', '\x0c', '\x55', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x5a', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x88', '\x00', '\x00', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
    '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x02', '\x00', '\x00'
}

local bmc_chip = {
    WriteRead = true,
    Read = function(self, ctx, read_offset, read_len)
        local rsp_data = {}
        read_offset = read_offset + 1
        read_len = read_len - 1

        for i = 0, read_len do
            rsp_data[#rsp_data + 1] = bmc_chip_data[read_offset + i]
        end

        return table.concat(rsp_data)
    end,
    Write = function(self, ctx, write_offset, data)
        write_offset = write_offset + 1

        for i, v in ipairs(data) do
            bmc_chip_data[write_offset + i - 1] = string.char(v)
        end
        return true
    end
}

local cpld_chip = {
    WriteRead = true,
    Read = function(self, ctx, read_offset, read_len)
        local rsp_data = {}
        read_offset = read_offset + 1
        read_len = read_len - 1

        for i = 0, read_len do
            rsp_data[#rsp_data + 1] = cpld_chip_data[read_offset + i]
        end

        return table.concat(rsp_data)
    end,
    Write = function(self, ctx, write_offset, data)
        write_offset = write_offset + 1

        for i, v in ipairs(data) do
            cpld_chip_data[write_offset + i - 1] = string.char(v)
        end
        return true
    end
}

local mds_obj = {
    SlotID = 1,
    RefBMCChip = bmc_chip,
    RefCPLDChip = cpld_chip,
    ManagerFirmwareVersion = '',
    smbus = {}
}

function TestHypercard:setupClass()
    self.hypercard_obj = hypercard_obj.new(mds_obj, '010101')
    self.hypercard_obj.smbus = hypercard_smbus_cfg.new()
end

function TestHypercard:teardownClass()
end

function TestHypercard:test_can_add_object()
    lu.assertEquals(self.hypercard_obj.obj.SlotID, 1)
    lu.assertNotEquals(self.hypercard_obj.BMCChip, nil)
    lu.assertNotEquals(self.hypercard_obj.CPLDChip, nil)
end

function TestHypercard:test_can_get_powerstatus()
    local power_state = self.hypercard_obj.smbus:CardInfo(self.hypercard_obj, 'PowerState')
    lu.assertEquals(power_state, 136)
end

function TestHypercard:test_can_update_static_property()
    self.hypercard_obj:update_static_property()
    lu.assertEquals(self.hypercard_obj.obj.CPLDProtocolVersion, { "1" })
    lu.assertEquals(self.hypercard_obj.obj.BOM, '0x5')
    lu.assertEquals(self.hypercard_obj.obj.LogicVersion, 'V12')
    lu.assertEquals(self.hypercard_obj.obj.ManagerFirmwareVersion, '1.18')
    lu.assertEquals(self.hypercard_obj.obj.BIOSVersion, '1.9')
    lu.assertEquals(self.hypercard_obj.obj.IMUVersion, '1.9')
    lu.assertEquals(self.hypercard_obj.obj.MemorySizeGiB, 32)
    lu.assertEquals(self.hypercard_obj.obj.DiskCapacityGiB, 240)
end

function TestHypercard:test_can_update_dynamic_property()
    self.hypercard_obj:update_dynamic_property()
    lu.assertEquals(self.hypercard_obj.obj.SerialDirection, 2)
    lu.assertEquals(self.hypercard_obj.obj.PowerWatts, 45)
    lu.assertEquals(self.hypercard_obj.obj.Inlet1TemperatureCelsius, 43)
    lu.assertEquals(self.hypercard_obj.obj.NetworkAdapterChipTemperatureCelsius, 57)
    lu.assertEquals(self.hypercard_obj.obj.NICSFPMaxTemperatureCelsius, 254)
    -- 设置其中一个光模块数组为异常，刷新数据
    bmc_chip_data[57] = '\xfd'
    self.hypercard_obj:update_dynamic_property()
    lu.assertEquals(self.hypercard_obj.obj.NICSFPMaxTemperatureCelsius, 253)
    -- 设置其中两个光模块数组为正常值，刷新数据
    bmc_chip_data[52] = '\x05'
    bmc_chip_data[55] = '\x09'
    self.hypercard_obj:update_dynamic_property()
    lu.assertEquals(self.hypercard_obj.obj.NICSFPMaxTemperatureCelsius, 9)
    lu.assertEquals(self.hypercard_obj.bmc_warning_reg, 254)
    lu.assertEquals(self.hypercard_obj.dpu_warning_reg, 255)
end

function TestHypercard:test_basic_func_with_error_property()
    local ok, err = pcall(function()
        return self.hypercard_obj.smbus:CardInfo(self.hypercard_obj, 'ErrorPorperty')
    end)
    lu.assertEquals(ok, false)
    local err_info <const> = 'the property is not supported'
    local ret = string.find(err or '', err_info)
    lu.assertNotEquals(ret, nil)
end

function TestHypercard:test_basic_func_with_error_chip()
    local test_obj = {
        CPLDChip = cpld_chip
    }
    local ok, err = pcall(function()
        return self.hypercard_obj.smbus:CardInfo(test_obj, 'PowerState')
    end)
    lu.assertEquals(ok, true)
    lu.assertEquals(err, 136)
    test_obj.CPLDChip = {}
    ok, err = pcall(function()
        return self.hypercard_obj.smbus:CardInfo(test_obj, 'PowerState')
    end)
    local err_info <const> = 'the chip.* is invalid'
    local ret = string.find(err or '', err_info)
    lu.assertEquals(ok, false)
    lu.assertNotEquals(ret, nil)
end

function TestHypercard:test_reset_sdi_card()
    local ret = self.hypercard_obj:reset_sdi_card()
    lu.assertEquals(ret, true)
end

function TestHypercard:test_set_dpu_power_state_case()
    local ret = self.hypercard_obj:set_dpu_power_state(0)
    lu.assertEquals(ret, true)

    ret = self.hypercard_obj:set_dpu_power_state(1)
    lu.assertEquals(ret, true)

    ret = self.hypercard_obj:set_dpu_power_state(3)
    lu.assertEquals(ret, false)
end

function TestHypercard:test_set_dpu_serial_direction_case()
    local ret = self.hypercard_obj:set_dpu_serial_direction(0)
    lu.assertEquals(ret, true)

    ret = self.hypercard_obj:set_dpu_serial_direction(1)
    lu.assertEquals(ret, true)

    ret = self.hypercard_obj:set_dpu_serial_direction(2)
    lu.assertEquals(ret, true)

    ret = self.hypercard_obj:set_dpu_serial_direction(3)
    lu.assertEquals(ret, true)

    ret = self.hypercard_obj:set_dpu_serial_direction(-1)
    lu.assertEquals(ret, false)

    ret = self.hypercard_obj:set_dpu_serial_direction(4)
    lu.assertEquals(ret, false)
end

function TestHypercard:test_basic_func_with_chip_but_without_read_or_write_method()
    local test_obj = {
        CPLDChip = cpld_chip
    }
    local ok, err = pcall(function()
        return self.hypercard_obj.smbus:CardInfo(test_obj, 'PowerState')
    end)
    lu.assertEquals(ok, true)
    lu.assertEquals(err, 136)

    -- 有chip，但chip不可写
    test_obj = {
        CPLDChip = {
            WriteRead = true,
            Read = {}
        }
    }
    ok, err = pcall(function()
        return self.hypercard_obj.smbus:CardInfo(test_obj, 'PowerState')
    end)
    local err_info <const> = 'the chip.* is invalid'
    local ret = string.find(err or '', err_info)
    lu.assertEquals(ok, false)
    lu.assertNotEquals(ret, nil)

    -- 有chip，但chip不可读
    test_obj = {
        CPLDChip = {
            WriteRead = true,
            Read = {}
        }
    }
    ok, err = pcall(function()
        return self.hypercard_obj.smbus:CardInfo(test_obj, 'PowerState')
    end)
    ret = string.find(err or '', err_info)
    lu.assertEquals(ok, false)
    lu.assertNotEquals(ret, nil)
end

function TestHypercard:test_basic_func_read_but_failed()
    local test_obj = {
        CPLDChip = {
            WriteRead = true,
            Read = {},
            Write = {}
        }
    }
    local ok, err = pcall(function()
        return self.hypercard_obj.smbus:CardInfo(test_obj, 'PowerState')
    end)
    local err_info <const> = 'chip read failed'
    local ret = string.find(err or '', err_info)
    lu.assertEquals(ok, false)
    lu.assertNotEquals(ret, nil)
end

function TestHypercard:test_basic_func_write_but_failed()
    local test_obj = {
        CPLDChip = {
            WriteRead = true,
            Read = {},
            Write = {}
        }
    }
    local ok, err = pcall(function()
        return self.hypercard_obj.smbus:CardInfo(test_obj, 'CPLDCheckWrite', { { 0x55 } })
    end)
    local err_info <const> = 'chip write failed'
    local ret = string.find(err or '', err_info)
    lu.assertEquals(ok, false)
    lu.assertNotEquals(ret, nil)
end

function TestHypercard:test_basic_func_write_but_without_data()
    local test_obj = {
        CPLDChip = {
            WriteRead = true,
            Read = {},
            Write = {}
        }
    }
    local ok, err = pcall(function()
        return self.hypercard_obj.smbus:CardInfo(test_obj, 'CPLDCheckWrite')
    end)
    local err_info <const> = 'write failed, write data is nil'
    local ret = string.find(err or '', err_info)
    lu.assertEquals(ok, false)
    lu.assertNotEquals(ret, nil)
end

function TestHypercard:test_get_cpld_check_result()
    local ret = self.hypercard_obj:get_cpld_check_result()
    lu.assertEquals(ret, false)

    self.hypercard_obj.obj.HeartBeatLoss = 1
    ret = self.hypercard_obj:get_cpld_check_result()
    lu.assertEquals(ret, true)
end

function TestHypercard:test_cpld_check()
    local ok = pcall(function()
        return self.hypercard_obj:start_cpld_check_task()
    end)
    lu.assertEquals(ok, true)
end

function TestHypercard:test_start_update_property_task()
    local ok = pcall(function()
        return self.hypercard_obj:start_update_property_task()
    end)
    lu.assertEquals(ok, true)
end

function TestHypercard:test_start_update_heartbeat_status_task()
    local ok = pcall(function()
        return self.hypercard_obj:start_update_heartbeat_status_task()
    end)
    lu.assertEquals(ok, true)
end

function TestHypercard:test_bmc_alarm_task()
    local bmc_error_code_list = {
        [0x01] = 'BoardVoltageAbnormalWarning',
        [0x02] = 'DPUBootFailWarning',
        [0x04] = 'DPUThermalTripErrorCritial',
        [0x08] = 'DPUResetWarning',
        [0x10] = 'DPUMCEErrorCritial'
    }
    local EVENT_DPU_FAULT_WARNING_KEY_ID <const> = 'PCIeCard.DPUFaultWarning'
    local EVENT_DPU_FAULT_CRITICAL_KEY_ID <const> = 'PCIeCard.DPUFaultCritical'
    local bmc_error_key_id_list = {
        [0x01] = EVENT_DPU_FAULT_WARNING_KEY_ID,
        [0x02] = EVENT_DPU_FAULT_WARNING_KEY_ID,
        [0x04] = EVENT_DPU_FAULT_CRITICAL_KEY_ID,
        [0x08] = EVENT_DPU_FAULT_WARNING_KEY_ID,
        [0x10] = EVENT_DPU_FAULT_CRITICAL_KEY_ID
    }
    local ok = pcall(function()
        return self.hypercard_obj:alarm_task(224, bmc_error_code_list, bmc_error_key_id_list)
    end)
    lu.assertEquals(ok, true)
    local ret = self.hypercard_obj:update_alarm_status(self.hypercard_obj.obj.DeviceName,
        'BoardVoltageAbnormalWarning')
    lu.assertEquals(ret, 0)
    ret = self.hypercard_obj:update_alarm_status(self.hypercard_obj.obj.DeviceName, 'DPUBootFailWarning')
    lu.assertEquals(ret, 0)
    ret = self.hypercard_obj:update_alarm_status(self.hypercard_obj.obj.DeviceName, 'DPUThermalTripErrorCritial')
    lu.assertEquals(ret, 0)
    ret = self.hypercard_obj:update_alarm_status(self.hypercard_obj.obj.DeviceName, 'DPUResetWarning')
    lu.assertEquals(ret, 0)
    ret = self.hypercard_obj:update_alarm_status(self.hypercard_obj.obj.DeviceName, 'DPUMCEErrorCritial')
    lu.assertEquals(ret, 0)

end

function TestHypercard:test_dpu_alarm_task()
    local dpu_error_code_list = {
        [0x01] = 'DDRUncorrectedECCErrorCritical',
        [0x02] = 'DDRCorrectableECCErrorWarning',
        [0x04] = 'DPUSRAM2bitECCCritical',
        [0x08] = 'DPUMEPErrorWarning',
        [0x10] = 'DPUMEErrorWarning',
        [0x20] = 'DPUDPEErrorWarning',
        [0x40] = 'DPUPCIECorrectableErrorWarning',
        [0x80] = 'DPUPCIEUncorrectedErrorCritical'
    }
    local EVENT_DPU_MAINCHIP_FAULT_WARNING_KEY_ID <const> = 'PCIeCard.DPUMainChipFaultWarning'
    local EVENT_DPU_MAINCHIP_FAULT_CRITICAL_KEY_ID <const> = 'PCIeCard.DPUMainChipFaultCritical'
    local dpu_error_key_id_list = {
        [0x01] = EVENT_DPU_MAINCHIP_FAULT_CRITICAL_KEY_ID,
        [0x02] = EVENT_DPU_MAINCHIP_FAULT_WARNING_KEY_ID,
        [0x04] = EVENT_DPU_MAINCHIP_FAULT_CRITICAL_KEY_ID,
        [0x08] = EVENT_DPU_MAINCHIP_FAULT_WARNING_KEY_ID,
        [0x10] = EVENT_DPU_MAINCHIP_FAULT_WARNING_KEY_ID,
        [0x20] = EVENT_DPU_MAINCHIP_FAULT_WARNING_KEY_ID,
        [0x40] = EVENT_DPU_MAINCHIP_FAULT_WARNING_KEY_ID,
        [0x80] = EVENT_DPU_MAINCHIP_FAULT_CRITICAL_KEY_ID
    }
    local ok = pcall(function()
        return self.hypercard_obj:alarm_task(0, dpu_error_code_list, dpu_error_key_id_list)
    end)
    lu.assertEquals(ok, true)
    local ret = self.hypercard_obj:update_alarm_status(self.hypercard_obj.obj.DeviceName,
        'DDRUncorrectedECCErrorCritical')
    lu.assertEquals(ret, 0)
    ret = self.hypercard_obj:update_alarm_status(self.hypercard_obj.obj.DeviceName, 'DDRCorrectableECCErrorWarning')
    lu.assertEquals(ret, 0)
    ret = self.hypercard_obj:update_alarm_status(self.hypercard_obj.obj.DeviceName, 'DPUSRAM2bitECCCritical')
    lu.assertEquals(ret, 0)
    ret = self.hypercard_obj:update_alarm_status(self.hypercard_obj.obj.DeviceName, 'DPUMEPErrorWarning')
    lu.assertEquals(ret, 0)
    ret = self.hypercard_obj:update_alarm_status(self.hypercard_obj.obj.DeviceName, 'DPUMEErrorWarning')
    lu.assertEquals(ret, 0)
    ret = self.hypercard_obj:update_alarm_status(self.hypercard_obj.obj.DeviceName, 'DPUDPEErrorWarning')
    lu.assertEquals(ret, 0)
    ret = self.hypercard_obj:update_alarm_status(self.hypercard_obj.obj.DeviceName, 'DPUPCIECorrectableErrorWarning')
    lu.assertEquals(ret, 0)
    ret = self.hypercard_obj:update_alarm_status(self.hypercard_obj.obj.DeviceName, 'DPUPCIEUncorrectedErrorCritical')
    lu.assertEquals(ret, 0)
end
