-- 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.

-- Description: fructrl rpc test
require 'skynet.manager'
local skynet = require 'skynet'
local sd_bus = require 'sd_bus'
local log = require 'mc.logging'
local m_enums = require 'types.enums'
local utils = require 'mc.utils'
local test_common = require 'test_common.utils'
local bs = require 'mc.bitstring'
local json = require 'cjson'
local file_sec = require 'utils.file'
require 'fructrl.json_types.SystemsFruCtrl'
require 'fructrl.json_types.SystemsPowerOnLock'
require 'fructrl.json_types.Manufacture'
local m_mdb = require 'mc.mdb'
local ctx = require 'mc.context'
local ctx_new = ctx.get_context_or_default()

log:setLevel(log.INFO)

local function prepare_test_data()
    log:info('prepare test data')
    local test_data_dir = skynet.getenv('TEST_DATA_DIR')
    os.execute('mkdir -p ' .. test_data_dir)

    local dir_list = {'apps/fructrl/mds', 'sr', 'usr/lib64', 'data', '/apps/ipmi_core/mds'}
    for _, path in pairs(dir_list) do
        os.execute('mkdir -p ' .. test_data_dir .. path)
    end
    utils.copy_file('test/integration/datatocheck_default.dat',
        test_data_dir .. 'datatocheck_default.dat')
    utils.copy_file('test/integration/test_data/root.sr', test_data_dir .. 'sr/root.sr')
    utils.copy_file('test/integration/test_data/14100513_EXU_01.sr', test_data_dir .. 'sr/14100513_EXU_01.sr')
    utils.copy_file('test/integration/test_data/14100513_BCU_01.sr', test_data_dir .. 'sr/14100513_BCU_01.sr')
    utils.copy_file('mds/schema.json', test_data_dir .. 'apps/fructrl/mds/schema.json')
    utils.copy_file('temp/usr/lib64/mock/libsoc_adapter_it.so',
        test_data_dir .. 'usr/lib64/libsoc_adapter.so')
    utils.copy_file('temp/opt/bmc/apps/ipmi_core/mds/schema.json',
        test_data_dir .. '/apps/ipmi_core/mds/schema.json')
    os.execute('tar -xzvf temp/test_data/apps/hwproxy/mockdata.tar.gz -C ' .. test_data_dir .. 'data')

    -- 准备 hwdiscovery 的测试数据
    os.execute('mkdir -p ' .. test_data_dir .. '/apps/hwdiscovery/mds')
    utils.copy_file('temp/opt/bmc/apps/hwdiscovery/mds/schema.json',
        test_data_dir .. '/apps/hwdiscovery/mds/schema.json')

    -- 准备power_state测试文件夹
    os.execute('mkdir -p ' .. test_data_dir .. 'power_states')
end

local function clear_test_data(exit_test)
    log:info('clear test data')
    local cwd = utils.realpath('.')
    local test_data_dir = cwd .. '/' .. skynet.getenv('TEST_DATA_DIR')
    if exit_test then
        skynet.timeout(0, function()
            skynet.sleep(20)
            skynet.abort()
            utils.remove_file(test_data_dir)
        end)
    else
        utils.remove_file(test_data_dir)
    end
end

local function test_intf_system_poweron(obj)
    print('============ PowerCtrlType.On start ============')
    local rsp = obj:PowerCtrl_PACKED(ctx_new, tostring(m_enums.PowerCtrlType.On),
        tostring(m_enums.RestartCause.ChassisControlCommand))
    assert(rsp.CmdResult == 0, 'response is not equal 0')
    print('============ PowerCtrlType.On end ============')
end

local function test_intf_system_poweroff(obj)
    print('============ PowerCtrlType.GracefulShutdown start ============')
    local rsp = obj:PowerCtrl_PACKED(ctx_new, tostring(m_enums.PowerCtrlType.GracefulShutdown),
        tostring(m_enums.RestartCause.ChassisControlCommand))
    assert(rsp.CmdResult == 0, 'response is not equal 0')
    print('============ PowerCtrlType.GracefulShutdown end ============')
end

local function test_intf_system_powerforceoff(obj)
    print('============ PowerCtrlType.ForceOff start ============')
    local rsp = obj:PowerCtrl_PACKED(ctx_new, tostring(m_enums.PowerCtrlType.ForceOff),
        tostring(m_enums.RestartCause.WatchdogExpiration))
    assert(rsp.CmdResult == 0, 'response is not equal 0')
    print('============ PowerCtrlType.ForceOff end ============')
end

local function test_intf_system_powercycle(obj)
    print('============ PowerCtrlType.PowerCycle start ============')
    local ok, err = obj.pcall:PowerCtrl(ctx_new, m_enums.PowerCtrlType.PowerCycle,
        m_enums.RestartCause.ChassisControlCommand)
    assert(not ok, "ok: " .. tostring(ok) .. " err: " .. tostring(err))
    print('============ PowerCtrlType.PowerCycle end ============')
end

local function test_intf_system_forcerestart(obj)
    print('============ PowerCtrlType.ForceRestart start ============')
    local rsp = obj:PowerCtrl_PACKED(ctx_new, tostring(m_enums.PowerCtrlType.ForceRestart),
        tostring(m_enums.RestartCause.ChassisControlCommand))
    assert(rsp.CmdResult == 0, 'response is not equal 0')
    print('============ PowerCtrlType.ForceRestart end ============')
end

local function test_intf_system_setaclost(obj)
    print('============ SetACLost start ============')
    local rsp = obj:SetACLost_PACKED(ctx_new, 1)
    assert(rsp.CmdResult == 0, 'response is not equal 0')
    print('============ SetACLost end ============')
end

-- 封装ipmi发送函数
local function ipmi_send_msg(bus, netfn, cmd, str)
    -- PKG_IPMI是固定的IPMI协议
    local PKG_IPMI = bs.new('<<_,_:2,DestNetFn:6,_:3/unit:8,Cmd,Payload/string>>')
    local ipmi_req = {
        DestNetFn = netfn,
        Cmd = cmd,
        Payload = str .. "\x00" -- 必须添加最后一位校验
    }
    local req = PKG_IPMI:pack(ipmi_req)
    local ctx_ipmi = json.encode({ChanType = 1, Instance = 0})
    local ok, rsp = pcall(bus.call, bus, 'bmc.kepler.ipmi_core', '/bmc/kepler/IpmiCore',
        'bmc.kepler.IpmiCore', 'Route', 'a{ss}ayay', ctx_new, req, ctx_ipmi)
    return ok, rsp
end

local function test_ipmi_powerctrl(bus)
    -- chassis ctrl
    local netfn = 0x00
    local cmd = 0x02
    local str = "\x01"
    local ok, rsp = ipmi_send_msg(bus, netfn, cmd, str)
    assert(ok, rsp)
    -- OEM get restart cause
    netfn = 0x30
    cmd = 0x92
    str = "\xdb\x07\x00\x11\x00"
    ok, rsp = ipmi_send_msg(bus, netfn, cmd, str)
    assert(ok, rsp)
    -- CMD get restart cause
    netfn = 0x00
    cmd = 0x07
    str = ""
    ok, rsp = ipmi_send_msg(bus, netfn, cmd, str)
    assert(ok, rsp)
end

local function test_ipmi_globalpowerctrl(bus)
    -- cmd fructrl
    local netfn = 0x2c
    local cmd = 0x04
    -- 全域重启
    local str = "\x00\x04\x00"
    local ok, rsp = ipmi_send_msg(bus, netfn, cmd, str)
    assert(ok, rsp)
    -- 全域下电再上电
    str = "\x00\x04\x06"
    ok, rsp = ipmi_send_msg(bus, netfn, cmd, str)
    assert(ok, rsp)
    -- 全域下电
    str = "\x00\x04\x05"
    ok, rsp = ipmi_send_msg(bus, netfn, cmd, str)
    assert(ok, rsp)
end

local function test_manufacture(obj)
    assert(obj.Slot == 0)
    local rsp
    -- 未开始，获取结果
    rsp = obj:GetResult_PACKED(ctx_new)
    assert(rsp.Status == "Non")
    -- 开始测试，未按按钮，获取结果
    obj:Start_PACKED(ctx_new)
    rsp = obj:GetResult_PACKED(ctx_new)
    assert(rsp.Status == "Failed")
    -- 停止测试，获取结果
    obj:Stop_PACKED(ctx_new)
    rsp = obj:GetResult_PACKED(ctx_new)
    assert(rsp.Status == "Non")
    assert(rsp.Description == "")
    log:info('Test power button finished.')
end

local function test_poweron_lock(obj, action)
    local rsp = obj:SetPowerOnLock_PACKED(ctx_new, action, 20, 'upgrade', 'cpld1')
    obj:SetPowerOnLock_PACKED(ctx_new, action, 20, 'upgrade', 'vpd1')
    obj:SetPowerOnLock_PACKED(ctx_new, action, 0xffff, 'upgrade', 'vpd1')
    obj:SetPowerOnLock_PACKED(ctx_new, action, 0xffff, 'active', 'cpld1')
    assert(rsp.CmdResult == 0, 'response is not equal 0')
end

local function test_poweron_bios(obj_lock, obj_ctrl)
    log:notice('==========test_poweron_bios start============')
    obj_lock:SetPowerOnLock_PACKED(ctx_new, true, 20, 'Upgrade', 'bios')
    ctx_new.Interface = 'CLI'
    local ok = obj_ctrl.pcall:PowerCtrl_PACKED(ctx_new, tostring(m_enums.PowerCtrlType.On),
        tostring(m_enums.RestartCause.ChassisControlCommand))
    assert(not ok)
    obj_lock:SetPowerOnLock_PACKED(ctx_new, false, 0xffff, 'Upgrade', 'bios')
    log:notice('==========test_poweron_bios end==============')
end

local function test_power_strategy_exceptions(obj, reason)
    local rsp = obj:SetPowerOnStrategyExceptions_PACKED(ctx_new, reason, 'Yes', 'Once', 1)
    assert(rsp.CmdResult == 0, 'response is not equal 0')
    -- 更新策略
    local ret = obj:SetPowerOnStrategyExceptions_PACKED(ctx_new, reason, 'No', 'Once', 1)
    assert(ret.CmdResult == 0, 'response is not equal 0')
end

local function test_on_dump(bus)
    log:notice('================ test on_dump start ================')
    -- 当前回调函数不支持代理对象访问，这地方直接使用busctl访问方式进行测试
    local service = 'bmc.kepler.fructrl'
    local path = '/bmc/kepler/fructrl/MicroComponent'
    local intf = 'bmc.kepler.MicroComponent.Debug'
    local dump = skynet.getenv('TEST_DATA_DIR') .. 'dump'
    local c = ctx.new('IT', 'Admin', '127.0.0.1')
    bus:call(service, path, intf, 'Dump', 'a{ss}s', c, dump)
    local f = file_sec.open_s(dump .. '/fructrl_info.txt', 'r')
    assert(f)
    f:close()
    log:notice('================ test on_dump completed ================')
end

local function test_on_import(bus)
    local type = 'configuration'
    local service = 'bmc.kepler.fructrl'
    local path = '/bmc/kepler/fructrl/MicroComponent'
    local intf = 'bmc.kepler.MicroComponent.ConfigManage'
    local c = ctx.new('IT', 'Admin', '127.0.0.1')
    local data = json.encode(
        {
            ConfigData = {
                Payload = {
                    PowerRestorePolicy = {Value = 'AlwaysPowerOn', Import = true},
                    PowerOffTimeout = {Value = 590, Import = true},
                    PowerDelayMode = {Value = 'DefaultDelay', Import = true},
                    PowerDelayCount = {Value = 1200, Import = true},
                    PowerOffTimeoutEN = {Value = 1, Import = true},
                    PwrButtonLock = {Value = false, Import = true}
                },
            }
        }
    )
    bus:call(service, path, intf, 'Import', 'a{ss}ss', c, data, type)
    data = json.encode(
        {
            ConfigData = {
                CustomSettings = {
                    BMCSet_POWERRESTOR = {Value = 'poweroff', Import = true},
                    BMCSet_PowerButtonLockStatus = {Value = 0, Import = true},
                    BMCSet_PowerRestoreDelayMode = {Value = 'DefaultDelay', Import = true},
                    BMCSet_PowerRestoreDelayTime = {Value = 1200, Import = true}
                },
            }
        }
    )
    type = 'custom'
    bus:call(service, path, intf, 'Import', 'a{ss}ss', c, data, type)
end

local function test_on_import_type(bus)
    local type = 'configuration'
    local service = 'bmc.kepler.fructrl'
    local path = '/bmc/kepler/fructrl/MicroComponent'
    local intf = 'bmc.kepler.MicroComponent.ConfigManage'
    local c = ctx.new('IT', 'Admin', '127.0.0.1')
    local data = json.encode(
        {
            ConfigData = {
                Payload = {
                    PowerRestorePolicy = {Value = 'AlwaysPowerOn', Import = false},
                    PowerOffTimeout = {Value = 590, Import = false},
                    PowerDelayMode = {Value = 'DefaultDelay', Import = false},
                    PowerDelayCount = {Value = 1200, Import = false},
                    PowerOffTimeoutEN = {Value = 1, Import = false},
                    PwrButtonLock = {Value = false, Import = false}
                },
            }
        }
    )
    bus:call(service, path, intf, 'Import', 'a{ss}ss', c, data, type)
end

local function test_on_export(bus)
    local type = 'configuration'
    local service = 'bmc.kepler.fructrl'
    local path = '/bmc/kepler/fructrl/MicroComponent'
    local intf = 'bmc.kepler.MicroComponent.ConfigManage'
    local c = ctx.new('IT', 'Admin', '127.0.0.1')
    local ret = bus:call(service, path, intf, 'Export', 'a{ss}s', c, type)
    assert(ret)
    local data = json.decode(ret)
    assert(data)
    assert(data.ConfigData)
    assert(data.ConfigData.Payload)
    local payload_group = data.ConfigData.Payload
    assert(payload_group.PowerRestorePolicy)
    assert(payload_group.PowerOffTimeout)
    assert(payload_group.PowerDelayMode)
    assert(payload_group.PowerDelayCount)
    assert(payload_group.PowerOffTimeoutEN)
    assert(not payload_group.PwrButtonLock)

    -- 验证装备定制化配置导出
    type = 'custom'
    ret = bus:call(service, path, intf, 'Export', 'a{ss}s', c, type)
    assert(ret)
    data = json.decode(ret)
    assert(data)
    assert(data.ConfigData)
    local customsettings = data.ConfigData.CustomSettings
    assert(customsettings.BMCSet_POWERRESTOR)
    assert(customsettings.BMCSet_PowerButtonLockStatus)
    assert(customsettings.BMCSet_PowerRestoreDelayMode)
    assert(customsettings.BMCSet_PowerRestoreDelayTime)
end

local function test_get_ipmi_acpi(bus)
    local netfn = 0x06
    local cmd = 0x07
    local str = ""
    local ok, rsp = ipmi_send_msg(bus, netfn, cmd, str)
    assert(ok, rsp)
end

local function test_set_ipmi_acpi(bus)
    local netfn = 0x06
    local cmd = 0x06
    local str = "\x00\x00"
    local ok, rsp = ipmi_send_msg(bus, netfn, cmd, str)
    assert(ok, rsp)
end

local function test_poweron_function()
    local fructrl = require('fructrl.fructrl')
    log:info("================ test test_poweron_function start ================")
    local ok, _ = pcall(function ()
        local fru_obj = fructrl.new({PowerCtrl = function() end}, 1)
        fru_obj:create_obj_callback('PowerButton',{IsValid = true,FruID = 3},1)
        local data = {}
        data.obj = {}
        data.obj.LastPowerEvent = 0x00
        data.poweron_from_ipmi_flag = false
        data.set_last_power_event = function(self, last_power_event)
            return true
        end

        data.get_PowerState = function(self)
            return tostring(m_enums.PowerStatus.ON)
        end
        fru_obj.poweron(data)
    end)
    assert(ok)
    log:info("================ test test_poweron_function end ================")
end

local function test_execute_therm_trip_callback()
    local fructrl = require('fructrl.fructrl')
    log:info("================ test test_execute_therm_trip_callback start ================")
    local ok, rsp = pcall(function()
        local fru = fructrl.new({},1)
        local obj = {
            PwrOnLocked = false,
            get_mdb_object = function(self, key)
                return {
                    property_before_change = {on = function() end},
                    property_changed = {on = function() end}
                }
            end,
            PowerCtrl = function() end
        }

        fru:set_fru_obj(obj, {})

        -- 覆盖无操作分支
        fru:create_obj_callback("ThermTrip", {
            SetThermTrip = 0,
            SetThermTrip2 = 0,
            Actions = 0,
            TimeDelaySeconds = 30,
            property_changed ={on = function() end}}, 1)
        fru.therm_trip:therm_trip_check(1)

        -- 覆盖上电分支
        fru:create_obj_callback("ThermTrip", {
            SetThermTrip = 0,
            SetThermTrip2 = 0,
            Actions = 1,
            TimeDelaySeconds = 30,
            property_changed ={on = function() end}}, 1)
        fru.therm_trip:therm_trip_check(1)

        -- 覆盖异常分支
        fru:create_obj_callback("ThermTrip", {
            SetThermTrip = 0,
            SetThermTrip2 = 0,
            Actions = 2,
            TimeDelaySeconds = 30,
            property_changed ={on = function() end}}, 1)
        fru.therm_trip:therm_trip_check(1)
    end)
    assert(ok, rsp)
    log:info("================ test test_execute_therm_trip_callback end ================")
end

local function test_is_power_locked_by_upgrade()
    log:info("================ test test_is_power_locked_by_upgrade start ================")
    local pwr_on_lock = require 'fructrl.pwr_lock'
    local obj = pwr_on_lock.new({system_id = 1, PwrOnLockedRecord = {}}, 'on')
    local ok, rsp = pcall(function()
        obj:is_power_locked_by_upgrade()
    end)
    assert(ok, rsp)
    log:info("================ test test_is_power_locked_by_upgrade end ================")
end

local function test_check_poweron()
    local fructrl = require('fructrl.fructrl')
    log:info("================ test test_check_poweron start ================")
    local fru_obj = fructrl.new({},1)
    local obj = {
        PwrOnLocked = false,
        system_id = 1,
        FruType = "",
        get_mdb_object = function(self, key)
            return {
                property_before_change = {on = function() end},
                property_changed = {on = function() end}
            }
        end,
        PowerCtrl = function() end,
        PowerState = "ON",
        LastPowerEvent = 1
    }

    fru_obj:set_fru_obj(obj, {})
    fru_obj:create_obj_callback("PowerButton", {}, 01)
    assert(fru_obj:poweron())
    local ok, rsp = pcall(function()
        fru_obj:props_changed_callback("PanelPowerButtonEnabled", true)
    end)
    assert(ok, rsp)
    log:info("================ test test_check_poweron end ================")
end

local function test_pwr_restore()
    log:info("================ test test_pwr_restore start ================")
    local pwr_restore = require 'pwr_restore'
    local c_pwr_restore = pwr_restore.new()
    assert(c_pwr_restore:get_bmc_reset_type(), 'get bmc reset type failed')
    log:info("================ test test_pwr_restore end ================")
end

local function test_button_evt()
    log:info("================ test test_button_evt start ================")
    local fructrl_obj = {
        obj = {
            get_mdb_object = function(self)
                return {
                    property_changed = {
                        on = function(self, cb)
                            cb()
                        end
                    }
                }
            end
        },
        get_PwrOnLocked = function(self)
            return true
        end,
        get_PowerOffLocked = function (self)
            return false
        end,
        get_PanelPowerButtonEnabled = function(self)
            return true
        end
    }
    local button_evt = require 'button_evt'
    button_evt.fructrl = fructrl_obj
    button_evt.set_PwrButtonLock = function(...) end
    local ok, _ = pcall(function()
        button_evt:listen_pwr_lock()
        button_evt:update_pwr_button_lock()
    end)
    assert(ok)
    log:info("================ test test_button_evt end ================")
end

local function test_thermaltrip_listen()
    log:info("================ test test_thermaltrip_listen start ================")
    local fructrl = require 'fructrl.fructrl'
    local fructrl_obj = fructrl.new(nil, nil)
    fructrl_obj.obj = {}
    fructrl_obj.obj.ActionModified = false
    local ok, rsp = pcall(function()
        fructrl_obj:thermaltrip_listen("Action", 1, nil)
    end)
    assert(ok, rsp)
    fructrl_obj.obj.ActionModified = false
    ok, rsp = pcall(function()
        fructrl_obj:thermaltrip_listen("Action", 1, "1")
    end)
    assert(ok, rsp)

    fructrl_obj.obj.TimeDelaySecondsModified = false
    local ok, rsp = pcall(function()
        fructrl_obj:thermaltrip_listen("TimeDelaySeconds", 1, nil)
    end)
    assert(ok, rsp)
    fructrl_obj.obj.TimeDelaySecondsModified = false
    local ok, rsp = pcall(function()
        fructrl_obj:thermaltrip_listen("TimeDelaySeconds", 1, "1")
    end)
    assert(ok, rsp)
    log:info("================ test test_thermaltrip_listen end ================")
end

local function test_init_thermaltrip_action()
    log:info("================ test init_thermaltrip_action start ================")
    local fructrl = require 'fructrl.fructrl'
    local fructrl_obj = fructrl.new(nil, nil)
    fructrl_obj.obj = {}
    fructrl_obj.therm_trip = {
        get_thermtrip_actions = function()
            return 1
        end
    }
    fructrl_obj.obj.ActionModified = false
    local ok, rsp = pcall(function()
        fructrl_obj:init_thermaltrip_action()
    end)
    assert(ok, rsp)
    log:info("================ test init_thermaltrip_action end ================")
end

local function test_ac_cycle_boundary_conditions()
    log:notice('================ test ac_cycle_boundary_conditions start ================')
    local ac_cycle = require 'ac_cycle'
    local accycle = ac_cycle
    accycle.objs = {
        {
            ACValue = 1,
            ACWpValue = 0
        }
    }

    local ok, rsp = pcall(function()
        accycle:ac_down()
    end)
    assert(ok, rsp)
    log:notice('================ test ac_cycle_boundary_conditions end ================')
end

local function test_poweroff_after_set_power_off_lock(obj_ctrl, bus)
    print('============ Test 4 situations after set power_off_lock start ============')
    local ok, ret = pcall(function()
        obj_ctrl:PowerCtrl_PACKED(ctx_new, tostring(m_enums.PowerCtrlType.GracefulShutdown),
        tostring(m_enums.RestartCause.ChassisControlCommand))
    end)
    assert(not ok, ret)

    ok, ret = pcall(function()
        obj_ctrl:PowerCtrl_PACKED(ctx_new, tostring(m_enums.PowerCtrlType.ForceOff),
        tostring(m_enums.RestartCause.ChassisControlCommand))
    end)
    assert(not ok, ret)

    ok, ret = pcall(function()
        obj_ctrl:PowerCtrl_PACKED(ctx_new, m_enums.PowerCtrlType.PowerCycle,
        m_enums.RestartCause.ChassisControlCommand)
    end)
    assert(not ok, ret)

    ok, ret = pcall(function()
        obj_ctrl:PowerCtrl_PACKED(ctx_new, m_enums.PowerCtrlType.ForcePowerCycle,
        m_enums.RestartCause.ChassisControlCommand)
    end)
    assert(not ok, ret)

    -- cmd fructrl
    local netfn = 0x00
    local cmd = 0x02

    -- force off
    local str = "\x00"
    local rsp
    ok, rsp = pcall(function()
        ipmi_send_msg(bus, netfn, cmd, str)
    end)
    assert(ok, rsp)
    -- force power cycle
    str = "\x02"
    ok, rsp = pcall(function()
        ipmi_send_msg(bus, netfn, cmd, str)
    end)
    assert(ok, rsp)
    -- forcecycle
    str = "\x06"
    ok, rsp = pcall(function()
        ipmi_send_msg(bus, netfn, cmd, str)
    end)
    assert(ok, rsp)
    -- 全域下电
    str = "\x05"
    ok, rsp = pcall(function()
        ipmi_send_msg(bus, netfn, cmd, str)
    end)
    assert(ok, rsp)
    print('============ Test 4 situations after set power_off_lock end ============')
end

-- 测试 PowerOffLock 相关方法
local function test_poweroff_lock(bus, obj, obj_ctrl)
    print('============ Test PowerOffLock start ============')
    -- 测试设置下电锁
    local ok = pcall(function()
        obj:SetPowerOffLock_PACKED(ctx_new, true, 30, 'maintenance', 'system1')
    end)
    assert(ok, 'SetPowerOffLock failed')
    -- 测试覆盖永久锁场景
    ok = pcall(function()
        obj:SetPowerOffLock_PACKED(ctx_new, true, 0xffff, 'emergency', 'system1')
    end)
    assert(ok, 'Set permanent PowerOffLock failed')
    test_poweroff_after_set_power_off_lock(obj_ctrl, bus)
    -- 测试解锁
    ok = pcall(function()
        obj:SetPowerOffLock_PACKED(ctx_new, false, 30, 'maintenance', 'system1')
    end)
    assert(ok, 'SetPowerOffLock failed')
    ok = pcall(function()
        obj:SetPowerOffLock_PACKED(ctx_new, false, 1000, 'emergency', 'system1')
    end)
    assert(ok, 'Unlock PowerOffLock failed')

    skynet.sleep(200)
    print('============ Test PowerOffLock end ============')
end



local function test_payload()
    local bus = sd_bus.open_user(true)
    log:info('================ test start ================')
    skynet.sleep(500) -- 延时等待资源上树
    local obj_ctrl = m_mdb.get_object(bus, '/bmc/kepler/Systems/1/FruCtrl/FruCtrl_1_0_0101',
        'bmc.kepler.Systems.FruCtrl')
    local obj_lock = m_mdb.get_object(bus, '/bmc/kepler/Systems/1/FruCtrl/FruCtrl_1_0_0101',
        'bmc.kepler.Systems.PowerOnLock')
    local obj_manu = m_mdb.get_object(bus, '/bmc/kepler/Manufacture/OperateTest/DftPowerButton/DftPowerButton_1_0101',
        'bmc.kepler.Manufacture')
    skynet.sleep(500) -- 延时等待资源上树

    print('============ test RPC method start ============ ')
    -- 上电
    test_intf_system_poweron(obj_ctrl)
    -- 系统复位
    test_intf_system_forcerestart(obj_ctrl)
    -- 闪断设置aclost值
    test_intf_system_setaclost(obj_ctrl)
    print('============ test RPC method end ============ ')

    print('============ test ipmi method start ============ ')
    -- ipmi测试
    test_intf_system_poweron(obj_ctrl)
    skynet.sleep(100)
    test_ipmi_powerctrl(bus)
    skynet.sleep(100)
    test_ipmi_globalpowerctrl(bus)
    print('============ test ipmi method end ============ ')
    -- 上电锁测试
    test_poweron_lock(obj_lock, true)
    skynet.sleep(200)
    test_poweron_lock(obj_lock, false)
    test_poweron_bios(obj_lock, obj_ctrl)
    skynet.sleep(200)
    -- 额外通电开机
    test_power_strategy_exceptions(obj_ctrl, 'test')
    -- 下电
    test_intf_system_poweroff(obj_ctrl)
    -- 强制下电
    test_intf_system_powerforceoff(obj_ctrl)
    -- 装备测试
    test_manufacture(obj_manu)
    -- 日志测试
    test_on_dump(bus)
    -- 导入导出
    test_on_import(bus)
    test_on_export(bus)

    test_set_ipmi_acpi(bus)
    test_get_ipmi_acpi(bus)

    test_poweron_function()

    test_execute_therm_trip_callback()

    test_is_power_locked_by_upgrade()
    test_check_poweron()

    test_pwr_restore()

    test_button_evt()

    test_thermaltrip_listen()

    test_init_thermaltrip_action()

    test_ac_cycle_boundary_conditions()

    require('test_multihost').test_entry(bus)
    require('test_chassis_power_ctrl').test_entry(bus)

    local poweroff_lock_obj = m_mdb.get_object(bus, '/bmc/kepler/Systems/1/FruCtrl/FruCtrl_1_0_0101',
        'bmc.kepler.FruCtrl.PowerOffLock')

    test_poweroff_lock(bus, poweroff_lock_obj, obj_ctrl)

    skynet.call('fructrl', 'lua', 'exit')
    log:info('================ test complete ================')
end

skynet.start(function()
    clear_test_data()
    prepare_test_data()
    test_common.dbus_launch()
    skynet.uniqueservice('sd_bus')
    skynet.uniqueservice('persistence/service/main')
    skynet.uniqueservice('maca/service/main')
    -- payload初始化依赖hwproxy完成上电
    skynet.uniqueservice('hwproxy/service/main')
    skynet.uniqueservice('hwdiscovery/service/main')
    skynet.uniqueservice('ipmi_core/service/main')
    skynet.uniqueservice('main')
    skynet.fork(function()
        local ok, err = pcall(test_payload)
        -- 注意做数据库写验证的时候，要注释掉下面一行
        clear_test_data(true)
        if not ok then
            error(err)
        end
    end)
end)
