-- Copyright (c) 2026 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 skynet = require('skynet')
require('skynet.manager')
local sdbus = require('sd_bus')
local log = require('mc.logging')
local utils = require('mc.utils')
local test_common = require('test_common.utils')
local mctp_lib = require('mctp_lib')
local utils_core = require('utils.core')
local net_ncsi_pkgs = require('net_ncsi_pkgs')
local net_nvme_pkgs = require('net_nvme_pkgs')
local lu = require('luaunit')
local mctp = require('mctp')
local context = require('mc.context')
local core = require('dbus.core')
local datas = require('test_data.test_datas')
local dbus_core = require('dbus.core')
local shm = dbus_core.SharedMemory
local DBusVariant = core.DBusVariant
local DBusMessage = core.DBusMessage
log:set_log_module_name('MctpTest')
log:set_debug_log_type('local')

local OS_POWER_ON_DELAY = 3000 -- 等待mctpd拉起监听服务
local RESET_DELAY = 800 -- 等待mctpd上下电动作完成
local indexs = { ['/dev/mctp'] = 1, ['/dev/mctp1'] = 1 }
-- local s_format = string.format
local shm_global_lock_exec = utils.shm_global_lock_exec

local g_pipe_fds = {}
local function close_pipe_fds()
    for _, fds in pairs(g_pipe_fds) do
        for _, fd in ipairs(fds) do
            mctp.close(fd)
        end
    end
end

local function prepare_test_data()
    local test_data_dir = skynet.getenv('TEST_DATA_DIR')
    os.execute('mkdir -p ' .. test_data_dir)
    local dir_list = { 'apps/hwproxy/mds', 'apps/hwdiscovery/mds', 'apps/mctpd/mds', 'sr' }
    for _, path in pairs(dir_list) do
        os.execute('mkdir -p ' .. test_data_dir .. '/' .. path)
    end
    utils.copy_file('test/integration/mctp.lua', 'temp/opt/bmc/apps/mctpd/lualib/mctp.lua')
    utils.copy_file(
        'temp/opt/bmc/apps/hwdiscovery/mds/schema.json',
        test_data_dir .. '/apps/hwdiscovery/mds/schema.json'
    )
    utils.copy_file('mds/schema.json', test_data_dir .. '/apps/mctpd/mds/schema.json')
    os.execute('cp' .. ' -r ' .. 'test/integration/test_data/sr' .. ' ' .. test_data_dir)
end

local function clear_test_data(exit_test)
    log:info('clear test data')
    local test_data_dir = skynet.getenv('TEST_DATA_DIR')
    if not exit_test then
        return utils.remove_file(test_data_dir)
    end

    close_pipe_fds()
    skynet.call('.mctpd', 'lua', 'exit')
    skynet.timeout(0, function()
        skynet.sleep(50)
        skynet.abort()
        utils.remove_file(test_data_dir)
    end)
end

local COMPUTE_APP_NAME = 'bmc.kepler.compute'
local function get_pmu_path(system_id)
    return string.format('/bmc/kepler/Systems/%s/Pmu', system_id)
end
local PMU_INTERFACE = 'bmc.kepler.Systems.Pmu'
local OBJ_INTERFACE = 'bmc.kepler.Object.Properties'
local function register_pmu_mdb_object(system_id)
    system_id = system_id or 1
    shm_global_lock_exec(function()
        local shm_tree = shm.get_tree(COMPUTE_APP_NAME)
        local shm_obj = shm_tree:register_object(get_pmu_path(system_id))
        shm_obj:register_interface(false, OBJ_INTERFACE)
        local shm_intf = shm_obj:register_interface(false, PMU_INTERFACE)
        local shm_p = shm_intf:add_p('Status', 'y')
        shm_p:set_value(0)
    end)
end

local BIOS_APP_NAME = 'bmc.kepler.bios'
local function get_smbios_path(system_id)
    return string.format('/bmc/kepler/Systems/%s/SmBios', system_id)
end
local SMBIOS_INTERFACE = 'bmc.kepler.Systems.SmBios'
local function register_smbios_mdb_object(system_id)
    system_id = system_id or 1
    shm_global_lock_exec(function()
        local shm_tree = shm.get_tree(BIOS_APP_NAME)
        local shm_obj = shm_tree:register_object(get_smbios_path(system_id))
        shm_obj:register_interface(false, OBJ_INTERFACE)
        local shm_intf = shm_obj:register_interface(false, SMBIOS_INTERFACE)
        local shm_p = shm_intf:add_p('SmBiosStatus', 'y')
        shm_p:set_value(0)
    end)
end

local SOCTRL_APP_NAME = 'bmc.kepler.soctrl'
local ENV_PATH = '/bmc/kepler/Managers/1/SOC/Env'
local ENV_INTERFACE = 'bmc.kepler.Managers.SOC.Env'
local function register_env_mdb_object()
    shm_global_lock_exec(function()
        local shm_tree = shm.get_tree(SOCTRL_APP_NAME)
        local shm_obj = shm_tree:register_object(ENV_PATH)
        shm_obj:register_interface(false, OBJ_INTERFACE)
        local shm_intf = shm_obj:register_interface(false, ENV_INTERFACE)
        local shm_p = shm_intf:add_p('SystemIds', 'au')
        shm_p:set_value({ 1, 2 })
    end)
end

local function new_signal(bus, app_name, path, intf, prop, v)
    local msg = DBusMessage.new_signal(path, 'org.freedesktop.DBus.Properties', 'PropertiesChanged')
    msg:append('sa{sv}as', intf, { [prop] = DBusVariant.new_byte(v) }, {})
    bus.bus:notify_signal(msg)
    shm_global_lock_exec(function()
        local shm_tree = shm.get_tree(app_name)
        local shm_obj = shm_tree:find_object(path)
        local shm_intf = shm_obj:find_interface(intf)
        local shm_p = shm_intf:find_p(prop)
        shm_p:set_value(v)
    end)
end

local function print_data(data, path)
    local index = indexs[path]
    local system_id = path == '/dev/mctp' and 1 or 2
    log:notice('====================[System%s]%s====================', system_id, datas[index].name)
    local hex_str = utils.to_hex(data)
    for line in hex_str:gmatch('([^\r\n]+)') do
        log:notice_easy(line)
    end
end

local function start_self_discovery(system_id)
    local path = '/dev/mctp'
    if system_id == 2 then
        path = '/dev/mctp1'
    end
    local index = indexs[path]
    print_data(datas[index].data, path)
    mctp.send_data(g_pipe_fds[path][2], datas[index].data)
    indexs[path] = index + 1
end

-- 测试Pmu状态属性变更监听
local function test_monitor_pmu_status(bus, system_id)
    system_id = system_id or 1
    log:notice('============== test monitor pmu status start ===============')
    local app_name = 'bmc.kepler.mctpd'
    local path = '/bmc/kepler/Systems/' .. system_id .. '/Mctp/Routing/8'
    local intf = 'bmc.kepler.Systems.Mctp.PCIeRoutingEntry'
    -- 模拟下电
    new_signal(bus, COMPUTE_APP_NAME, get_pmu_path(system_id), PMU_INTERFACE, 'Status', 0)
    skynet.sleep(RESET_DELAY)
    local err =
        bus:pcall(app_name, path, OBJ_INTERFACE, 'GetWithContext', 'a{ss}ssv', context.new(), intf, 'StartingEid')
    lu.assertNotIsNil(err)
    -- 模拟上电
    new_signal(bus, COMPUTE_APP_NAME, get_pmu_path(system_id), PMU_INTERFACE, 'Status', 1)
    skynet.sleep(RESET_DELAY)
    start_self_discovery(system_id)
    skynet.sleep(RESET_DELAY)
    local rsp
    err, rsp =
        bus:pcall(app_name, path, OBJ_INTERFACE, 'GetWithContext', 'a{ss}ssv', context.new(), intf, 'StartingEid')
    lu.assertEquals(err, nil)
    lu.assertEquals(rsp:value(), 9)
    log:notice('============== test monitor pmu status complete ===============')
end

-- 测试SmBios状态属性变更监听
local function test_monitor_smbios_status(bus, system_id)
    system_id = system_id or 1
    log:notice('============== test monitor smobios status start ===============')
    local app_name = 'bmc.kepler.mctpd'
    local path = '/bmc/kepler/Systems/' .. system_id .. '/Mctp/Routing/8'
    local intf = 'bmc.kepler.Systems.Mctp.PCIeRoutingEntry'
    -- 模拟下电
    new_signal(bus, BIOS_APP_NAME, get_smbios_path(system_id), SMBIOS_INTERFACE, 'SmBiosStatus', 4)
    skynet.sleep(RESET_DELAY)
    local err =
        bus:pcall(app_name, path, OBJ_INTERFACE, 'GetWithContext', 'a{ss}ssv', context.new(), intf, 'StartingEid')
    lu.assertNotIsNil(err)
    -- 模拟上电
    new_signal(bus, BIOS_APP_NAME, get_smbios_path(system_id), SMBIOS_INTERFACE, 'SmBiosStatus', 3)
    skynet.sleep(RESET_DELAY)
    start_self_discovery(system_id)
    skynet.sleep(RESET_DELAY)
    local rsp
    err, rsp =
        bus:pcall(app_name, path, OBJ_INTERFACE, 'GetWithContext', 'a{ss}ssv', context.new(), intf, 'StartingEid')
    lu.assertEquals(err, nil)
    lu.assertEquals(rsp:value(), 9)
    log:notice('============== test monitor smobios status complete ===============')
end

local function test_reset_mctpd(bus, system_id)
    log:notice('============== test mctp reset start ===============')
    skynet.call('.mctpd', 'lua', 'reset')
    skynet.sleep(RESET_DELAY)
    -- 驱动复位后模拟OS重新上下电,触发mctpd发现状态重建
    new_signal(bus, COMPUTE_APP_NAME, get_pmu_path(system_id), PMU_INTERFACE, 'Status', 0)
    skynet.sleep(RESET_DELAY)
    new_signal(bus, COMPUTE_APP_NAME, get_pmu_path(system_id), PMU_INTERFACE, 'Status', 1)
    skynet.sleep(RESET_DELAY)
    start_self_discovery(system_id)
    skynet.sleep(RESET_DELAY)
    log:notice('============== test mctp reset complete ===============')
end

local ncsi_eps = {}
local nvme_eps = {}
local spdm_eps = {}
local function test_add_mctp_transport(bus, system_id)
    log:notice('================ test add mctp transport ================')
    local module_name = 'MctpTest'
    local phy_addr = 8
    local ncsi_msg_type = mctp_lib.MCTP_MESSAGE_TYPE_NCSI
    local nvme_msg_type = mctp_lib.MCTP_MESSAGE_TYPE_NVME
    local spdm_msg_type = mctp_lib.MCTP_MESSAGE_TYPE_SPDM
    local ok, ncsi_endpoint, ncsi_transport =
        pcall(mctp_lib.get_endpoint_and_transport, bus, module_name, phy_addr, ncsi_msg_type, nil, system_id)
    local _, nvme_endpoint, _ =
        pcall(mctp_lib.get_endpoint_and_transport, bus, module_name, phy_addr, nvme_msg_type, nil, system_id)
    local _, spdm_endpoint, _ =
        pcall(mctp_lib.get_endpoint_and_transport, bus, module_name, phy_addr, spdm_msg_type, nil, system_id)
    if not ok or not ncsi_transport or not ncsi_endpoint then
        error('unable to get transport and endpoint: ' .. ncsi_endpoint)
    end

    ncsi_eps[system_id] = ncsi_endpoint
    nvme_eps[system_id] = nvme_endpoint
    spdm_eps[system_id] = spdm_endpoint

    lu.assertEquals(ncsi_endpoint.ServiceName, module_name)
    lu.assertEquals(ncsi_endpoint.TargetEid, 9)
    lu.assertEquals(ncsi_endpoint.TargetPhyAddr, phy_addr)
    lu.assertEquals(ncsi_endpoint.MessageType, mctp_lib.MCTP_MESSAGE_TYPE_NCSI)
    lu.assertEquals(spdm_endpoint.MessageType, mctp_lib.MCTP_MESSAGE_TYPE_SPDM)
end

local function test_get_mac_addr(system_id)
    log:notice('================ test get mac addr ================')
    local data = net_ncsi_pkgs.mac_addr_req()
    local req_ctx = {
        PacketType = tostring(0x50),
        ChannelId = tostring(0),
        PackageId = tostring(0),
    }
    local rsp_ctx = {
        PacketType = tostring(0xD0),
    }
    local ncsi_ep = ncsi_eps[system_id]
    local rsp_ok, rsp = pcall(ncsi_ep.Request, ncsi_ep, context.get_context_or_default(), data, 0, req_ctx, rsp_ctx)
    lu.assertEquals(rsp_ok, true)
    lu.assertNotIsNil(rsp)

    local v = net_ncsi_pkgs.mac_addr_rsp(rsp)
    lu.assertNotIsNil(v)
    lu.assertEquals(v.mac_addr_count, 1)
    lu.assertEquals(v.mac_addrs[1], { mac_addr = '04:8C:16:59:C8:6E' })
end

local function test_nvme_vpd_read(system_id)
    log:notice('================ test nvme vpd read ================')
    local data = net_nvme_pkgs.nvme_commad_vpd_read_req()
    local req_ctx = {
        IC = tostring(0x01),
        MsgType = tostring(0x01),
        CmdSlot = tostring(0x00),
    }
    local rsp_ctx = {}
    local nvme_ep = nvme_eps[system_id]
    local rsp_ok, rsp = pcall(nvme_ep.Request, nvme_ep, context.get_context_or_default(), data, 0, req_ctx, rsp_ctx)
    lu.assertEquals(rsp_ok, true)
    lu.assertEquals(rsp, net_nvme_pkgs.exp_nvme_commad_vpd_read_rsp)
end

local function test_nvme_replay(system_id)
    log:notice('================ test nvme reply ================')
    local data = net_nvme_pkgs.nvme_replay_control_primitive_req()
    local req_ctx = {
        IC = tostring(0x01),
        MsgType = tostring(0x00),
        CmdSlot = tostring(0x00),
    }
    local rsp_ctx = {}
    local nvme_ep = nvme_eps[system_id]
    local rsp_ok, rsp = pcall(nvme_ep.Request, nvme_ep, context.get_context_or_default(), data, 0, req_ctx, rsp_ctx)
    lu.assertEquals(rsp_ok, true)
    lu.assertEquals(rsp, net_nvme_pkgs.exp_nvme_replay_rsp)
end

local function test_nvme_abort(system_id)
    log:notice('================ test nvme abort ================')
    local data = net_nvme_pkgs.nvme_abort_control_primitive_req()
    local req_ctx = {
        IC = tostring(0x01),
        MsgType = tostring(0x00),
        CmdSlot = tostring(0x00),
    }
    local rsp_ctx = {}
    local nvme_ep = nvme_eps[system_id]
    local rsp_ok, rsp = pcall(nvme_ep.Request, nvme_ep, context.get_context_or_default(), data, 0, req_ctx, rsp_ctx)
    lu.assertEquals(rsp_ok, true)
    lu.assertEquals(rsp, net_nvme_pkgs.exp_nvme_abort_rsp)
end

local function test_nvme_replay_without_retransmit(system_id)
    log:notice('================ test nvme reply without retransmit ================')
    local data = net_nvme_pkgs.nvme_replay_control_primitive_req()
    local req_ctx = {
        IC = tostring(0x01),
        MsgType = tostring(0x00),
        CmdSlot = tostring(0x00),
    }
    local rsp_ctx = {}
    local nvme_ep = nvme_eps[system_id]
    local rsp_ok, rsp = pcall(nvme_ep.Request, nvme_ep, context.get_context_or_default(), data, 0, req_ctx, rsp_ctx)
    lu.assertEquals(rsp_ok, true)
    lu.assertEquals(rsp, net_nvme_pkgs.exp_nvme_replay_rsp_without_retransmit)
end

local function test_nvme_abort_with_need_more_process(system_id)
    log:notice('================ test nvme abort with need more process ================')
    local data = net_nvme_pkgs.nvme_abort_control_primitive_req()
    local req_ctx = {
        IC = tostring(0x01),
        MsgType = tostring(0x00),
        CmdSlot = tostring(0x00),
    }
    local exp_rsp = '\x01\x00\x00\x00\x00\x55\x00\x00'
    local rsp_ctx = {}
    local nvme_ep = nvme_eps[system_id]
    local rsp_ok, rsp = pcall(nvme_ep.Request, nvme_ep, context.get_context_or_default(), data, 0, req_ctx, rsp_ctx)
    lu.assertEquals(rsp_ok, true)
    lu.assertEquals(rsp, exp_rsp)
end

local function test_spdm_request(system_id)
    log:notice('================ test spdm request ================')
    local data = '\x01\x02\x03\x04\x05\x06\x07\x08'
    local req_ctx = {
        Version = tostring(0x25),
        Code = tostring(0xfc),
        Param1 = tostring(0x01),
        Param2 = tostring(0x02),
    }
    local rsp_ctx = {}

    local exp_rsp = '\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A'
    local spdm_ep = spdm_eps[system_id]
    local rsp_ok, rsp = pcall(spdm_ep.Request, spdm_ep, context.get_context_or_default(), data, 0, req_ctx, rsp_ctx)
    lu.assertEquals(rsp_ok, true)
    lu.assertEquals(rsp, exp_rsp)
end

local function test_spdm_request_with_rsp_ctx(system_id)
    log:notice('================ test spdm request with rsp_ctx ================')
    local data = '\x01\x02\x03\x04\x05\x06\x07\x08'
    local req_ctx = {
        Version = tostring(0x25),
        Code = tostring(0xfc),
        Param1 = tostring(0x01),
        Param2 = tostring(0x02),
    }

    local rsp_ctx = { Code = tostring(0x1c) }
    local spdm_ep = spdm_eps[system_id]
    local exp_rsp = '\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A'
    local rsp_ok, rsp = pcall(spdm_ep.Request, spdm_ep, context.get_context_or_default(), data, 0, req_ctx, rsp_ctx)
    lu.assertEquals(rsp_ok, true)
    lu.assertEquals(rsp, exp_rsp)
end

local function test_spdm_send(system_id)
    log:notice('================ test spdm send ================')
    local data = '\x01\x02\x03\x04\x05'
    local req_ctx = {
        Version = tostring(0x36),
        Code = tostring(0xad),
        Param1 = tostring(0xcc),
        Param2 = tostring(0xd8),
    }
    local spdm_ep = spdm_eps[system_id]
    local rsp_ok = pcall(spdm_ep.Send, spdm_ep, context.get_context_or_default(), data, req_ctx)

    lu.assertEquals(rsp_ok, true)
end

local function test_spdm_request_without_req(system_id)
    log:notice('================ test spdm request without req ================')
    local data = '\x01\x02\x03\x04\x05\x06\x07'
    local req_ctx = {}
    local rsp_ctx = {}

    local exp_rsp = '\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A'
    local spdm_ep = spdm_eps[system_id]
    local rsp_ok, rsp = pcall(spdm_ep.Request, spdm_ep, context.get_context_or_default(), data, 0, req_ctx, rsp_ctx)
    lu.assertEquals(rsp_ok, true)
    lu.assertEquals(rsp, exp_rsp)
end

local function test_spdm_send_without_req(system_id)
    log:notice('================ test spdm send without req ================')
    local data = '\x01\x02\x03\x04\x05\x06\x07\x08'
    local req_ctx = {}
    local spdm_ep = spdm_eps[system_id]
    local rsp_ok = pcall(spdm_ep.Send, spdm_ep, context.get_context_or_default(), data, req_ctx)
    lu.assertEquals(rsp_ok, true)
end

local str = [[[MCTP Registry Info]
idx:0, module_name = MCTP, phy_addr = 0, msg_src_eid = 0, msg_type = 0, pid = %s, process_tag = 3224632059
       request total count: 13, success count: 4, timeout count: 5, req_failed count: 0, inner_req_error count: 0
idx:1, module_name = MctpTest, phy_addr = 8, msg_src_eid = 9, msg_type = 2, pid = %s, process_tag = 3224632059
       request total count: 1, success count: 1, timeout count: 0, req_failed count: 0, inner_req_error count: 0
idx:2, module_name = MctpTest, phy_addr = 8, msg_src_eid = 9, msg_type = 4, pid = %s, process_tag = 3224632059
       request total count: 5, success count: 5, timeout count: 0, req_failed count: 0, inner_req_error count: 0
idx:3, module_name = MctpTest, phy_addr = 8, msg_src_eid = 9, msg_type = 5, pid = %s, process_tag = 3224632059
       request total count: 5, success count: 5, timeout count: 0, req_failed count: 0, inner_req_error count: 0
[MCTP ROUTE TABLE Info]
EID:0x09, PHYADDR:0x0008, PHYSICAL_MEDIA_IDENTIFIER:0x0f(PCI 1.0,2.0,2.1,2.2,2.3,3.0,PCI-X 1.0, PCI-X 2.0)
the number of entry  in route table is 1
[MCTP Registry Info]
idx:0, module_name = MCTP, phy_addr = 0, msg_src_eid = 0, msg_type = 0, pid = %s, process_tag = 3224632059
       request total count: 8, success count: 4, timeout count: 0, req_failed count: 0, inner_req_error count: 0
idx:1, module_name = MctpTest, phy_addr = 8, msg_src_eid = 9, msg_type = 2, pid = %s, process_tag = 3224632059
       request total count: 1, success count: 1, timeout count: 0, req_failed count: 0, inner_req_error count: 0
idx:2, module_name = MctpTest, phy_addr = 8, msg_src_eid = 9, msg_type = 4, pid = %s, process_tag = 3224632059
       request total count: 5, success count: 5, timeout count: 0, req_failed count: 0, inner_req_error count: 0
idx:3, module_name = MctpTest, phy_addr = 8, msg_src_eid = 9, msg_type = 5, pid = %s, process_tag = 3224632059
       request total count: 5, success count: 5, timeout count: 0, req_failed count: 0, inner_req_error count: 0
[MCTP ROUTE TABLE Info]
EID:0x09, PHYADDR:0x0008, PHYSICAL_MEDIA_IDENTIFIER:0x0f(PCI 1.0,2.0,2.1,2.2,2.3,3.0,PCI-X 1.0, PCI-X 2.0)
the number of entry  in route table is 1]] .. '\n[MCTP_SEND_RECV_STATE_REGISTER Info]\r\n' .. 'mctp_state_reg_addr(0x0CD30024), mctp_state_reg_value(0x10000), mctp_link_state(Normal)\r\n' .. 'pcie_controller_state_reg_addr(0x0CD0E094), pcie_controller_state_reg_value(0xc0c82051), pcie_link_state(LINK_UP)\r\n'

local function test_mctp_dump(bus)
    local service = 'bmc.kepler.mctpd'
    local path = '/bmc/kepler/mctpd/MicroComponent'
    local intf = 'bmc.kepler.MicroComponent.Debug'
    local file_path = skynet.getenv('TEST_DATA_DIR')
    bus:call(service, path, intf, 'Dump', 'a{ss}s', context.new(), file_path)
    local file = io.open(file_path .. 'mctp_info', 'r')
    lu.assertNotIsNil(file)
    local data = file:read('a')
    file:close()
    local pid = utils_core.getpid()
    local expert_str = string.format(str, pid, pid, pid, pid, pid, pid, pid, pid)
    lu.assertEquals(data, expert_str)
end

local function test_run_host(bus, system_id)
    start_self_discovery(system_id)
    skynet.sleep(RESET_DELAY)
    test_monitor_pmu_status(bus, system_id)
    test_monitor_smbios_status(bus, system_id)
    test_reset_mctpd(bus, system_id)
    test_add_mctp_transport(bus, system_id)
    test_get_mac_addr(system_id)
    test_nvme_vpd_read(system_id)
    test_nvme_replay(system_id)
    test_nvme_abort(system_id)
    test_nvme_replay_without_retransmit(system_id)
    test_nvme_abort_with_need_more_process(system_id)
    test_spdm_request(system_id)
    test_spdm_request_with_rsp_ctx(system_id)
    test_spdm_send(system_id)
    test_spdm_request_without_req(system_id)
    test_spdm_send_without_req(system_id)
end

local function test_run()
    log:notice('================ test start ================')
    local bus = sdbus.open_user(true)
    skynet.sleep(OS_POWER_ON_DELAY)
    local system_id = 1
    new_signal(bus, COMPUTE_APP_NAME, get_pmu_path(system_id), PMU_INTERFACE, 'Status', 1)
    skynet.sleep(RESET_DELAY)
    test_run_host(bus, system_id)
    system_id = 2
    new_signal(bus, COMPUTE_APP_NAME, get_pmu_path(system_id), PMU_INTERFACE, 'Status', 1)
    skynet.sleep(RESET_DELAY)
    test_run_host(bus, system_id)
    test_mctp_dump(bus)
    log:notice('=============== test complete ==============')
end

local CMD = {}
local fd_path_map = {}
function CMD.set_fds(fd1, fd2, path)
    -- fd1: 读端,fd2: 写端
    g_pipe_fds[path] = { fd1, fd2 }
    fd_path_map[fd1] = path
end

local function check_next_data(index)
    local ret = index > #datas or datas[index].type == 'SEND' or datas[index].name == '[owner -> BMC] ep_discovery_req'
    return ret
end

function CMD.assert_data(data, fd)
    local path = fd_path_map[fd]
    local index = indexs[path]
    if index > #datas then
        return
    end

    if data == datas[index].data then
        print_data(data, path)
        index = index + 1
        indexs[path] = index
        if datas[index] and datas[index].type == 'SEND' or index > #datas then
            return
        else
            repeat
                print_data(datas[index].data, path)
                mctp.send_data(g_pipe_fds[path][2], datas[index].data)
                index = index + 1
                indexs[path] = index
            until check_next_data(index)
        end
    end
end

skynet.start(function()
    skynet.register('.mctp_test')
    skynet.dispatch('lua', function(_, _, cmd, ...)
        local f = assert(CMD[cmd])
        skynet.ret(skynet.pack(f(...)))
    end)
    clear_test_data()
    prepare_test_data()
    test_common.dbus_launch()
    skynet.uniqueservice('sd_bus')
    skynet.uniqueservice('persistence/service/main')
    register_pmu_mdb_object()
    register_smbios_mdb_object()
    register_pmu_mdb_object(2) -- 2: system_id
    register_smbios_mdb_object(2) -- 2: system_id
    register_env_mdb_object()
    skynet.uniqueservice('maca/service/main')
    skynet.uniqueservice('hwdiscovery/service/main')
    skynet.uniqueservice('main')
    skynet.fork(function()
        local ok, err = pcall(test_run)
        if not ok then
            error(err)
        end
        clear_test_data(true)
    end)
end)