-- 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 mock_err = {
    SUCCESS = 0,
    ERR_INVALID_FIELD = 0xCC, 
    ERR_INVALID_ID = 0xCB,
    ERR_OUT_OF_RANGE = 0xC9,
    ERR_CANNOT_RESPONSE = 0xD5,
    ipmi_error_map = function(err_code)
        return { code = err_code }
    end
}
package.loaded['sensor_error'] = mock_err

package.loaded['mc.logging'] = {
    error = function(...) end,
    warn = function(...) end,
    info = function(...) end,
    debug = function(...) end,
    notice = function(...) end
}

package.loaded['skynet'] = { sleep = function() end }
package.loaded['sensor.sensor_instance'] = {}
package.loaded['sensor.ipmi.ipmi'] = {}
package.loaded['sensor.sensor_common'] = {}
package.loaded['sensor.sensor_const'] = {}
package.loaded['sensor_utils'] = {}
package.loaded['sensor_operation'] = {}
package.loaded['shm_data_management'] = {}
package.loaded['sensor.sensor_management'] = nil
local sensor_mgmt = require 'sensor.sensor_management'

TestSensorIPMI = {}

local function create_mock_sensor(name, type, ent_id, ent_inst, raw_reading)
    return {
        sensor_clz = 'ThresholdSensor',
        mdb_obj = {
            SensorName = name,
            SensorType = type,
            EntityId = ent_id,
            EntityInstance = ent_inst,
            Reading = raw_reading
        },
        get_sensor_reading = function()
            return mock_err.SUCCESS, { Reading = raw_reading }
        end,
        
        get_dcmi_temperature_reading = function(self)
            local val = raw_reading & 0xFF
            local val_signed = (val > 127) and (val - 256) or val
            local dcmi_byte = 0
            if val_signed < 0 then
                dcmi_byte = 0x80 | (math.abs(val_signed) & 0x7F)
            else
                dcmi_byte = val_signed & 0x7F
            end
            return dcmi_byte, ent_inst
        end
    }
end

-- ================== 测试用例 ==================

function TestSensorIPMI:test_ipmi_get_temperature_readings()
    local mgnt_obj = {
        sensor_objs = {},
        ipmi_get_temperature_readings = sensor_mgmt.ipmi_get_temperature_readings
    }
   
    local sensors = {}
    
    table.insert(sensors, create_mock_sensor("Inlet_1", 0x01, 0x37, 1, 25))
    table.insert(sensors, create_mock_sensor("Inlet_2", 0x01, 0x37, 2, 0xFB))
    table.insert(sensors, create_mock_sensor("CPU_1", 0x01, 0x03, 1, 50))
    table.insert(sensors, create_mock_sensor("Board_1", 0x01, 0x07, 1, 30))

    table.insert(sensors, create_mock_sensor("Volt_1", 0x02, 0x37, 3, 12))

    local discrete = create_mock_sensor("Discrete_Temp", 0x01, 0x37, 4, 0)
    discrete.sensor_clz = 'DiscreteSensor'
    table.insert(sensors, discrete)

    mgnt_obj.sensor_objs = sensors

    local req = { GrpExtId = 0xDC, SensorType = 0x02 }
    local ok, ret = pcall(mgnt_obj.ipmi_get_temperature_readings, mgnt_obj, req, {})
    lu.assertFalse(ok, "Should fail with invalid SensorType")
    lu.assertEquals(ret.code, mock_err.ERR_INVALID_FIELD)

    req = { 
        GrpExtId = 0xDC, 
        SensorType = 0x01, 
        EntityID = 0x40,
        EntityInstance = 0x00, 
        EntityInstanceStart = 1 
    }
    ok, ret = pcall(mgnt_obj.ipmi_get_temperature_readings, mgnt_obj, req, {})
    lu.assertTrue(ok)
    lu.assertEquals(ret.CompletionCode, 0x00)
    lu.assertEquals(ret.AvailableInstances, 2) -- Inlet_1 和 Inlet_2
    
    local expected_data = string.char(0x19, 0x01, 0x85, 0x02)
    lu.assertEquals(ret.TempData, expected_data)

    local many_sensors = {}
    for i = 1, 10 do
        table.insert(many_sensors, create_mock_sensor("Inlet_" .. string.format("%02d", i), 0x01, 0x37, i, i))
    end
    mgnt_obj.sensor_objs = many_sensors

    req = { GrpExtId = 0xDC, SensorType = 0x01, EntityID = 0x40, EntityInstance = 0x00, EntityInstanceStart = 1 }
    ok, ret = pcall(mgnt_obj.ipmi_get_temperature_readings, mgnt_obj, req, {})
    lu.assertTrue(ok)
    lu.assertEquals(ret.AvailableInstances, 10) 
    lu.assertEquals(ret.TempDataCount, 8) 
    local last_byte_idx = 8 * 2
    lu.assertEquals(string.byte(ret.TempData, last_byte_idx), 0x08)

    req.EntityInstanceStart = 9
    ok, ret = pcall(mgnt_obj.ipmi_get_temperature_readings, mgnt_obj, req, {})
    lu.assertTrue(ok)
    lu.assertEquals(ret.TempDataCount, 2)
    lu.assertEquals(string.byte(ret.TempData, 1), 0x09)
    lu.assertEquals(string.byte(ret.TempData, 2), 0x09)

    req.EntityInstanceStart = 11
    ok, ret = pcall(mgnt_obj.ipmi_get_temperature_readings, mgnt_obj, req, {})
    lu.assertFalse(ok)
    lu.assertEquals(ret.code, mock_err.ERR_OUT_OF_RANGE)

    mgnt_obj.sensor_objs = sensors 
    
    req = { 
        GrpExtId = 0xDC, 
        SensorType = 0x01, 
        EntityID = 0x41,
        EntityInstance = 1, 
        EntityInstanceStart = 0 
    }
    ok, ret = pcall(mgnt_obj.ipmi_get_temperature_readings, mgnt_obj, req, {})
    lu.assertTrue(ok)
    lu.assertEquals(ret.AvailableInstances, 1) 
    lu.assertEquals(ret.TempDataCount, 1)
    lu.assertEquals(ret.TempData, string.char(0x32, 0x01))

    req.EntityInstance = 99
    ok, ret = pcall(mgnt_obj.ipmi_get_temperature_readings, mgnt_obj, req, {})
    lu.assertFalse(ok)
    lu.assertEquals(ret.code, mock_err.ERR_INVALID_ID)

    req = { 
        GrpExtId = 0xDC, 
        SensorType = 0x01, 
        EntityID = 0x99, 
        EntityInstance = 0x00, 
        EntityInstanceStart = 1 
    }
    ok, ret = pcall(mgnt_obj.ipmi_get_temperature_readings, mgnt_obj, req, {})
    lu.assertFalse(ok)
    lu.assertEquals(ret.code, mock_err.ERR_INVALID_ID)
end