-- 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_fan_object = require 'fan_object'
local c_fan_type_object = require 'fan_type_object'
local c_object_manage = require 'mc.orm.object_manage'
local c_tasks = require 'mc.orm.tasks'
local thermal_db = require 'thermal_mgmt.db'
local hook_tasks = require 'test_common.hook_tasks'
local test_utils = require 'test_utils'
local thermal_mgmt_utils = require 'thermal_mgmt_utils'
local fructl_data_keeping = require 'basic_cooling.data_keeping.fructl_data_keeping'

local identify_status<const> = {
    UNIDENTIFIED = 0,
    IDENTIFYING = 1,
    IDENTIFIED = 2,
    IDENTIFY_FAILED = 3
}

TestThermal = {}

function TestThermal:flush()
    c_tasks.get_instance():run_all_task()
    self.database.db:flush()
end

-- 框架收到自发现分发的对象后，在回调 on_add_object 函数前会装载持久化数据回对象
-- 多风扇板场景仅ObjectName能承载唯一性主键，在此时填充主键
c_fan_object.before_add_object = function(object)
    object.FanObjectName = object.ObjectName
end

local fan_obj = {
    FanId = 1,
    Slot = 1,
    Type = 0,
    Model = "32030389-004 8080+",
    Coefficient = 1,
    FanHealth = 0,
    FanEntity = "",
    FrontPresence = 1,
    RearPresence = 1,
    FrontSpeed = 5376,
    RearSpeed = 5120,
    FrontStatus = 0,
    RearStatus = 0,
    FrontRPM = 0,
    RearRPM = 0,
    HardwarePWM = 81,
    PWMPercentage = 0,
    ExpectedPWM = 81,
    FanAlarmLed = 0,
    BOM = "BOM 32030389-004",
    SystemId = 1,
    MaxSupportedPWM = 255,
    IdentifySpeedLevel = 30,
    PartNumber = "32030389-004",
    FrontMaxSpeed = 16500,
    RearMaxSpeed = 16500,
    IsTwins = true,
    FanPosition = 'CLU',
    SpeedStable = true,
    FunctionDesc = "",
    DeviceName = "",
    SetFanPWM = function ()
        return 1
    end,
        GetData = function ()
        
        end,
        GetItems = function ()
            
        end,
        SynDataAcquisitionEnable = function ()
            
        end,
        filter_type_by_twins = function ()

        end,
        refresh_fans_model = function ()

        end,
    Position = '010107',
    ObjectName = "Fan",
    FanObjectName = "Fan",
    ObjectIdentifier = {},
    PowerGood = 1,
    DualConnector = {},
    SingleConnector = {},
    __position = "010107",
    IdentifyStatus = 0
}

local fan_type  = {
    Name = "02314QEB 4056+",
    FanTypeObjectName = "FanType_4056P_010107",
    Index = 1,
    IsDefaultType = true,
    IsTwins = true,
    FrontMaxSpeed = 16500,
    RearMaxSpeed = 16500,
    PartNumber = "32030389-004",
    BOM = "BOM 32030389-004",
    SystemId = 1,
    IdentifyRangeLo = 4207,
    IdentifyRangeHigh = 5692,
    __position = "010107"
}

function TestThermal:setUp()
    self.database = thermal_db(':memory:')

    hook_tasks.hook()
    self.object_manage = c_object_manage.new(self.database)
    self.object_manage.app = self

    local fan_type_obj = test_utils.add_fan_type_obj(self.object_manage, 'FanType_1', fan_type)
    local obj = test_utils.add_fan_obj(self.object_manage, 'Fan_1', fan_obj)
    self.object_manage.mc:before_add_object(c_fan_object.__class_name, obj)
    self.object_manage.mc:add_object(c_fan_object.__class_name, obj, '010107')
    self.object_manage.mc:before_add_object(c_fan_type_object.__class_name, fan_type_obj)
    self.object_manage.mc:add_object(c_fan_type_object.__class_name, fan_type_obj, '010107')
    self.object_manage.mc:prepare_ok()
    self:flush()
end

function TestThermal:tearDown()
    c_object_manage.destroy()
    self.database.db:close()
    hook_tasks.unhook()
end

function TestThermal:test_ctor_init()
    local obj = c_fan_object.collection:find({
        FanId = 1
    })
    lu.assertEquals(obj.change_times, 0)
    lu.assertEquals(obj.last_pwm, 0)
    lu.assertEquals(obj.default_model, false)
    lu.assertEquals(obj.last_present, -1)
    lu.assertEquals(obj.model_source, 0)
    lu.assertEquals(obj.fan_model, false)
    lu.assertEquals(obj.mcu_upgrading, false)
    lu.assertEquals(obj.start_flag, false)
    lu.assertEquals(obj.fan_board, false)
    lu.assertEquals(obj.standbyfan, false)
    lu.assertEquals(obj.dual_connector, false)
    lu.assertEquals(obj.single_connector, false)
    lu.assertEquals(obj.persist_fan_id, 1)
    lu.assertEquals(obj.ExpectedPWM, 81)
end

function TestThermal:test_get_or_set_status_error()
    local obj = c_fan_object.collection:find({
        FanId = 1
    })
    lu.assertEquals(obj:get_status_error('front'), 0)
    lu.assertEquals(obj:get_status_error('rear'), 0)
    lu.assertEquals(obj.FrontStatus, 0)
    lu.assertEquals(obj.RearStatus, 0)

    obj:set_status_error('front', 1)
    lu.assertEquals(obj:get_status_error('front'), 1)
    lu.assertEquals(obj.FrontStatus, 2)

    obj:set_status_error('rear', -1)
    lu.assertEquals(obj:get_status_error('rear'), -1)
    lu.assertEquals(obj.RearStatus, 0)
end

function TestThermal:test_is_present()
    local obj = c_fan_object.collection:find({
        FanId = 1
    })
    lu.assertEquals(obj:is_present(), true)
end

function TestThermal:test_is_independence()
    c_fan_object.RearPresence = 1
    c_fan_object.FrontPresence = 1
    lu.assertEquals(c_fan_object:is_independence(), false)

    c_fan_object.FrontPresence = 0
    lu.assertEquals(c_fan_object:is_independence(), true)
end

function TestThermal:test_set_expected_pwm()
    local obj = c_fan_object.collection:find({
        FanId = 1
    })
    obj:set_expected_pwm(100.99)
    lu.assertEquals(obj.ExpectedPWM, 100)
end

function TestThermal:test_get_max_supported_pwm()
    local obj = c_fan_object.collection:find({
        FanId = 1
    })
    lu.assertEquals(obj:get_max_supported_pwm(), 255)

    obj.MaxSupportedPWM = 0
    lu.assertEquals(obj:get_max_supported_pwm(), 1000)

    obj.MaxSupportedPWM = nil
    lu.assertEquals(obj:get_max_supported_pwm(), 1000)
end

function TestThermal:test_get_or_set_error_count()
    local obj = c_fan_object.collection:find({
        FanId = 1
    })
    lu.assertEquals(obj:get_error_count('front'), 0)
    lu.assertEquals(obj:get_error_count('rear'), 0)

    obj:set_error_count('front', 1)
    lu.assertEquals(obj:get_error_count('front'), 1)

    obj:set_error_count('rear', -1)
    lu.assertEquals(obj:get_error_count('rear'), -1)
end

function TestThermal:test_change_times_add_once()
    local obj = c_fan_object.collection:find({
        FanId = 1
    })
    lu.assertEquals(obj.change_times, 0)
    obj:change_times_add_once()
    lu.assertEquals(obj.change_times, 1)
end

function TestThermal:test_is_identified()
    local obj = c_fan_object.collection:find({
        FanId = 1
    })
    obj.IdentifyStatus = 0
    lu.assertEquals(obj:is_identified(), false)

    obj.IdentifyStatus = 2
    lu.assertEquals(obj:is_identified(), true)
end

function TestThermal:test_update_fan_info()
    local obj = c_fan_object.collection:find({
        FanId = 1
    })
    local fan_type = {
        get_fan_type_name = function ()
            return  "02314QEB 4056+"
        end,
        get_type_part_num = function ()
            return  "02314QEB"
        end,
        get_type_bom_id = function ()
            return  "BOM 32030300-005"
        end,
        get_is_twins = function ()
            return  true
        end,
        get_front_max_speed = function ()
            return  23000
        end,
        get_rear_max_speed = function ()
            return  22760
        end,
    }

    obj:update_fan_info(fan_type)
    lu.assertEquals(obj.Model, "02314QEB 4056+")
    lu.assertEquals(obj.PartNumber, "02314QEB")
    lu.assertEquals(obj.BOM, "BOM 32030300-005")
    lu.assertEquals(obj.IsTwins, true)
    lu.assertEquals(obj.FrontMaxSpeed, 23000)
    lu.assertEquals(obj.RearMaxSpeed, 22760)
end

function TestThermal:test_set_mismatch()
    local obj = c_fan_object.collection:find({
        FanId = 1
    })
    obj.IdentifyStatus = 2
    obj:set_mismatch()
    lu.assertEquals(obj.status_error.front_error, 8)
    lu.assertEquals(obj.status_error.rear_error, 8)
end

function TestThermal:test_clear_mismatch()
    local obj = c_fan_object.collection:find({
        FanId = 1
    })
    obj.IdentifyStatus = 2
    obj:clear_mismatch()
    lu.assertEquals(obj.status_error.front_error, 0)
    lu.assertEquals(obj.status_error.rear_error, 0)
end

function TestThermal:test_recover_info_from_db()
    local obj = c_fan_object.collection:find({
        FanId = 1
    })
    local db_obj = {
        Type = 1,
        Model = "32030389-004 8080+",
        PartNumber = "32030389-004",
        BOM = "BOM 32030389-004",
        IsTwins = true,
        FrontMaxSpeed = 16500,
        RearMaxSpeed = 16500
    }
    obj:recover_info_from_db(db_obj)
    lu.assertEquals(obj.Type, 1)
    lu.assertEquals(obj.Model, "32030389-004 8080+")
    lu.assertEquals(obj.PartNumber, "32030389-004")
    lu.assertEquals(obj.BOM, "BOM 32030389-004")
    lu.assertEquals(obj.IsTwins, true)
    lu.assertEquals(obj.FrontMaxSpeed, 16500)
    lu.assertEquals(obj.RearMaxSpeed, 16500)
    lu.assertEquals(obj.model_source, 1)
end

function TestThermal:test_clear_fan_info()
    local obj = c_fan_object.collection:find({
        FanId = 1
    })
    obj:clear_fan_info()
    lu.assertEquals(obj.status_record.front_error_count, 0)
    lu.assertEquals(obj.status_record.rear_error_count, 0)
    lu.assertEquals(obj.change_times, 0)
    lu.assertEquals(obj.FrontRPM, 0)
    lu.assertEquals(obj.RearRPM, 0)
    lu.assertEquals(obj.FrontStatus, 0)
    lu.assertEquals(obj.RearStatus, 0)
    lu.assertEquals(obj.Model, '')
    lu.assertEquals(obj.PartNumber, '')
    lu.assertEquals(obj.SpeedStable, false)
    lu.assertEquals(obj.default_model, false)
    lu.assertEquals(obj.last_present, 1)
    lu.assertEquals(obj.IdentifyStatus, 0)
    lu.assertEquals(obj.status_error.front_error, 0)
    lu.assertEquals(obj.status_error.rear_error, 0)
    lu.assertEquals(obj.model_source, 0)
end

function TestThermal:test_clear_fan_status()
    local obj = c_fan_object.collection:find({
        FanId = 1
    })
    obj:clear_fan_status()
    lu.assertEquals(obj.status_error.front_error, 0)
    lu.assertEquals(obj.status_error.rear_error, 0)
end

function TestThermal:test_check_speed_change_for_status()
    local obj = c_fan_object.collection:find({
        FanId = 1
    })
    obj.change_times = 0
    obj.ExpectedPWM = 81
    obj.last_pwm = 60
    for _ = 1, 5 do
        obj:check_speed_change_for_status()
    end
    lu.assertEquals(obj.change_times, 5)
end

function TestThermal:test_fan_identify()
    local obj = c_fan_object.collection:find({
        FanId = 1
    })
    lu.assertEquals(obj:fan_identify(1), false)

    c_fan_object.set_pwm = function(pwm)
    end
    c_fan_object.filter_type_by_speed = function()
        return {1}, {1}
    end
    c_fan_object.fan_model_saved = function()
        return true
    end
    obj.IdentifyStatus = 1
    obj.IdentifySpeedLevel = 30
    lu.assertEquals(obj:fan_identify(1), true)

    c_fan_object.fan_model_saved = function()
        return false
    end
    obj.IdentifyStatus = 1
   lu.assertEquals(obj:fan_identify(1), false)

    obj.IdentifyStatus = 0
    lu.assertEquals(obj:fan_identify(1), false)
end

function TestThermal:test_identify_fans_type()
    local obj = c_fan_object.collection:find({
        FanId = 1
    })
    obj.Model = ''
    c_fan_object.fan_model_saved = function ()
        return "02314QEB 4056+"
    end
    c_fan_object.is_present = function ()
        return true
    end

    obj.standbyfan = true
    c_fan_object.fan_identify = function ()
        return true
    end
    thermal_mgmt_utils.get_chassis_type = function ()
        return 6
    end
    local ok = pcall(function ()
        return obj:identify_fans_type()
    end)
    obj.standbyfan = false
    obj.Model = "02314QEB 4056+"
    obj.IdentifyStatus = 0
    lu.assertEquals(ok, true)
end

function TestThermal:test_identify_fans_type_identify()
    local obj = c_fan_object.collection:find({
        FanId = 1
    })
    obj.Model = 'test'
    c_fan_object.fan_model_saved = function ()
        return "02314QEB 4056+"
    end
    c_fan_object.RearPresence = 1
    c_fan_object.last_present = 0

    obj.standbyfan = true
    c_fan_object.fan_identify = function ()
        return false
    end
    local ok = pcall(function ()
        return c_fan_object:identify_fans_type()
    end)
    c_fan_object.next_tick = function ()
    end
    thermal_mgmt_utils.operation_log = function ()
    end
    c_fan_object.fructl_listenning = function ()
    end
    c_fan_object.RearPresence = 0
    c_fan_object.last_present = 1
    c_fan_object.ObjectName = "Fan"
    c_fan_object.FanId = 1
    c_fan_object.FrontPresence = 0
    c_fan_object.fan_status_changed = {}
    c_fan_object.fan_status_changed.emit = function(...)
        return
    end
    c_fan_object.listen = function(c_fan_object, name, func)
        func()
    end
    local ok = pcall(function ()
        return c_fan_object:create_fan_listenning()
    end)
    lu.assertEquals(ok, true)

    c_fan_object.RearPresence = 1
    c_fan_object.last_present = 0
    c_fan_object.listen = function(c_fan_object, name, func)
        func()
    end
    local ok = pcall(function ()
        return c_fan_object:create_fan_listenning()
    end)
    lu.assertEquals(ok, true)
end

function TestThermal:test_fructl_data_changed()
    thermal_mgmt_utils.get_chassis_type = function ()
        return 6
    end
    local ok = pcall(function ()
        return fructl_data_keeping:action_after_property_changed(nil, nil, nil, 'On')
    end)
    lu.assertEquals(ok, true)
    thermal_mgmt_utils.get_chassis_type = function ()
        return nil
    end
    ok = pcall(function ()
        return fructl_data_keeping:action_after_property_changed(nil, nil, nil, 'On')
    end)
    lu.assertEquals(ok, true)
end