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

require 'skynet.manager'
local skynet = require 'skynet'
local sdbus = require 'sd_bus'
local test_common = require 'test_common.utils'
local log = require 'mc.logging'
local utils = require 'mc.utils'
local mdb = require 'mc.mdb'
local lu = require 'luaunit'
local test_vrd = require 'test_vrd'
local test_retimer = require 'test_retimer'
local test_manufacture = require 'test_manufacture'
local test_cpu = require 'test_cpu'
local test_gpu = require 'test_gpu'
local test_sr_prepare = require 'test_sr_prepare'
local test_ipmi = require 'test_ipmi'
local test_logic_fw = require 'test_cpld_self_test'
local test_mcu_log = require 'test_mcu_log'

local client = require 'general_hardware.client'

local project_dir = os.getenv('PROJECT_DIR')
local test_dir = project_dir .. '/test/integration/.test_temp_data/'
skynet.setenv('PROG_APP_PATH', test_dir .. 'apps')
skynet.setenv('PROG_CSR_PATH', test_dir .. 'sr')
skynet.setenv('DATA_CSR_PATH', test_dir .. 'data/sr')

local function prepare_test_data()
    local TEST_CSR_PATH < const > = 'test/integration/test_csr/'
    local TEST_SELF_MDS_PATH < const > = '/apps/general_hardware/mds'
    local TEST_DISC_MDS_PATH < const > = '/apps/hwdiscovery/mds'
    local TEST_FRUDATA_MDS_PATH < const > = '/apps/frudata/mds'
    local TEST_FRUCTRL_MDS_PATH < const > = '/apps/fructrl/mds'
    local TEST_USR_PATH < const > = 'usr/lib64'
    local TEST_DATA_PATH < const > = 'data'

    local project_dir = os.getenv('PROJECT_DIR')
    local test_dir = project_dir .. '/test/integration/.test_temp_data/'

    local absolute_test_data_dir = os.getenv('PROJECT_DIR') .. '/test/integration/.test_temp_data/'
    skynet.setenv('KSF_PATH', absolute_test_data_dir)
    skynet.setenv('KSF_BAK_PATH', absolute_test_data_dir)
    skynet.setenv('KSF_DEFAULT_PATH', absolute_test_data_dir)

    local relative_keystore_upgrade = skynet.getenv('REL_KEYSTORE_UPGRADE')
    local keystore_upgrade = os.getenv('PROJECT_DIR') .. '/' .. relative_keystore_upgrade
    skynet.setenv('KEYSTORE_UPGRADE', keystore_upgrade)

    log:info('== prepare test data')
    local test_data_dir = skynet.getenv('TEST_DATA_DIR')
    -- 拷贝本地CSR
    os.execute('mkdir -p ' .. test_data_dir .. '/sr')
    utils.copy_file(TEST_CSR_PATH .. 'root.csr', test_data_dir .. '/sr/root.sr')
    utils.copy_file(TEST_CSR_PATH .. '14100513_BCU_01.csr', test_data_dir .. '/sr/14100513_BCU_01.sr')
    utils.copy_file(TEST_CSR_PATH .. '14100513_ACU_01.csr', test_data_dir .. '/sr/14100513_ACU_01.sr')
    utils.copy_file(TEST_CSR_PATH .. '14100513_EXU_01.csr', test_data_dir .. '/sr/14100513_EXU_01.sr')
    utils.copy_file(TEST_CSR_PATH .. '14100513_SEU_01.sr', test_data_dir .. '/sr/14100513_SEU_01.sr')
    utils.copy_file(TEST_CSR_PATH .. '14220246_VF_9.sr', test_data_dir .. '/sr/14220246_VF_9.sr')
    utils.copy_file(TEST_CSR_PATH .. '14100513_CLU_01.sr', test_data_dir .. '/sr/14100513_CLU_01.sr')
    -- 拷贝模块自身的schema
    os.execute('mkdir -p ' .. test_data_dir .. TEST_SELF_MDS_PATH)
    utils.copy_file('mds/schema.json', test_data_dir .. TEST_SELF_MDS_PATH .. '/schema.json')
    -- 拷贝依赖模块的schema
    os.execute('mkdir -p ' .. test_data_dir .. TEST_DISC_MDS_PATH)
    utils.copy_file('temp/opt/bmc/apps/hwdiscovery/mds/schema.json',
        test_data_dir .. TEST_DISC_MDS_PATH .. '/schema.json')
    os.execute('mkdir -p ' .. test_data_dir .. '/apps/hwproxy/mds/')
    utils.copy_file('temp/opt/bmc/apps/hwproxy/mds/schema.json',
        test_data_dir .. '/apps/hwproxy/mds/schema.json')
    os.execute('mkdir -p ' .. test_data_dir .. TEST_FRUDATA_MDS_PATH)
    utils.copy_file('temp/opt/bmc/apps/frudata/mds/schema.json',
        test_data_dir .. TEST_FRUDATA_MDS_PATH .. '/schema.json')
        
    os.execute('mkdir -p ' .. test_data_dir .. TEST_FRUCTRL_MDS_PATH)
    utils.copy_file('temp/opt/bmc/apps/fructrl/mds/schema.json',
        test_data_dir .. TEST_FRUCTRL_MDS_PATH .. '/schema.json')

    -- 拷贝依赖文件
    os.execute('mkdir -p ' .. test_data_dir .. '/apps/ipmi_core/mds')
    utils.copy_file('temp/opt/bmc/apps/ipmi_core/mds/schema.json',
        test_data_dir .. '/apps/ipmi_core/mds/schema.json')
    utils.copy_file('temp/opt/bmc/apps/ipmi_core/mds/service.json',
        test_data_dir .. '/apps/ipmi_core/mds/service.json')
    os.execute('mkdir -p ' .. test_data_dir .. TEST_USR_PATH)
    os.execute('mkdir -p ' .. test_data_dir .. TEST_DATA_PATH)
    os.execute('tar -xzvf temp/test_data/apps/hwproxy/mockdata.tar.gz -C ' .. test_data_dir .. 'data')
    utils.copy_file('temp/usr/lib64/mock/libsoc_adapter_it.so',
        test_data_dir .. '/usr/lib64/libsoc_adapter.so')
    -- 拷贝秘钥管理模块依赖文件
    utils.copy_file('test/integration/data/datatocheck_default.dat', test_data_dir .. '/datatocheck_default.dat')
end

local function clear_test_data(exit_test)
    log:info('== clear test data')
    local test_data_dir = utils.realpath('.') .. '/' .. 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_exp_board(bus)
    -- ExpBoard
    local objs = mdb.get_sub_objects(bus, '/bmc/kepler/Systems/1/Boards/ExpBoard', 'bmc.kepler.Systems.Board')
    local exp_board = {board = objs:fold(function(obj)
        if obj.path:match('ExpBoard_1') then
            return obj, false
        end
    end)}
    lu.assertEquals(exp_board.board.Slot, 1)
    lu.assertEquals(exp_board.board.Name, 'BC83SMMB')
    lu.assertEquals(exp_board.board.Manufacturer, 'Huawei')
    lu.assertEquals(exp_board.board.Description, 'Expander Board')
    lu.assertEquals(exp_board.board.PartNumber, '0302023922')
    lu.assertEquals(exp_board.board.PcbVersion, '.A')
    lu.assertEquals(exp_board.board.LogicVersion, '0.01')
    lu.assertEquals(exp_board.board.MultiLogicVersion.CPLD1, '0.01')
    lu.assertEquals(exp_board.board.MultiLogicVersion.CPLD2, '0.01')
    lu.assertEquals(exp_board.board.MultiLogicVersion.FPGA, '0.01')
    lu.assertEquals(exp_board.board.FruID, 1)
    lu.assertEquals(exp_board.board.NodeId, 'chassisEXU1')
    lu.assertEquals(exp_board.board.DeviceName, 'EXU1')
    lu.assertEquals(exp_board.board.BoardType, 'ExpBoard')

    exp_board.unit = mdb.get_object(bus, exp_board.board.path, 'bmc.kepler.Systems.Board.Unit')
    lu.assertEquals(exp_board.unit.UID, '00000001010302023922')
    lu.assertEquals(exp_board.unit.Type, 'EXU')
end

local function test_cpu_board(bus)
    -- CpuBoard
    local objs = mdb.get_sub_objects(bus, '/bmc/kepler/Systems/1/Boards/CpuBoard', 'bmc.kepler.Systems.Board')
    local cpu_board = objs:fold(function(obj)
        if obj.path:match('CpuBoard_1') then
            return obj, false
        end
    end)
    log:error("========================MultiLogicVersion===========================================================%s", cpu_board.MultiLogicVersion.CPLD1)
    lu.assertEquals(cpu_board.MultiLogicVersion.CPLD1, '0.01')
    lu.assertEquals(cpu_board.MultiLogicVersion.CPLD2, '0.01')
end

local function test_hdd_backplane(bus)
    -- HddBackplane
    local objs = mdb.get_sub_objects(bus, '/bmc/kepler/Systems/1/Boards/HddBackplane', 'bmc.kepler.Systems.Board')
    local hdd_backplane = objs:fold(function(obj)
        if obj.path:match('HddBackplane_1') then
            return obj, false
        end
    end)
    lu.assertEquals(hdd_backplane.Slot, 1)
end

local function test_npu_board(bus)
    -- NpuBoard
    local objs = mdb.get_sub_objects(bus, '/bmc/kepler/Systems/1/Boards/NpuBoard', 'bmc.kepler.Systems.Board')
    local npu_board = objs:fold(function(obj)
        if obj.path:match('NpuBoard_1') then
            return obj, false
        end
    end)
    lu.assertEquals(npu_board.DeviceName, 'NpuBoard3')
end

local function test_fan_board(bus)
    -- FanBoard
    local objs = mdb.get_sub_objects(bus, '/bmc/kepler/Systems/1/Boards/FanBoard', 'bmc.kepler.Systems.Board')
    local fan_board = objs:fold(function(obj)
        if obj.path:match('FanBoard_1') then
            return obj, false
        end
    end)
    lu.assertEquals(fan_board.DeviceName, 'FanBoard1')
end

local function test_M2_board(bus)
    -- M2TransferCard
    local objs = mdb.get_sub_objects(bus, '/bmc/kepler/Systems/1/Boards/M2TransferCard', 'bmc.kepler.Systems.Board')
    local fan_board = objs:fold(function(obj)
        if obj.path:match('M2TransferCard_1') then
            return obj, false
        end
    end)
    lu.assertEquals(fan_board.DeviceName, 'M2TransferCard1')
end

-- 测试从资源树获取组件基本信息
local function test_get_unit_info(bus)
    log:info('- test_get_unit_info')
    require 'general_hardware.json_types.Board'
    require 'general_hardware.json_types.Unit'
    test_exp_board(bus)
    test_cpu_board(bus)
    test_hdd_backplane(bus)
    test_npu_board(bus)
    test_fan_board(bus)
    test_M2_board(bus)
end

-- 测试入口
local function main()
    log:notice('================ test general_hardware start ================')
    local bus = sdbus.open_user(true)
    log:notice('================ test ipmi ================')
    test_ipmi.test_ipmi_cmd(bus)
    log:notice('================ test get_unit_info ================')
    test_get_unit_info(bus)
    log:notice('================ test vrd ================')
    test_vrd.test_all_vrd_method(bus)
    log:notice('================ test retimer ================')
    -- retimer等待较慢，等待10s
    skynet.sleep(1000)
    test_retimer.test_retimer(bus)
    log:notice('================ test manufacture ================')
    test_manufacture.test_all_dft_method(bus)
    log:notice('================ test cpu ================')
    test_cpu.main(bus)
    log:notice('================ test mcu log ================')
    test_mcu_log.main(bus)
    skynet.call('.general_hardware', 'lua', 'exit')
    log:notice('================ test gpu ================')
    test_gpu.main(bus)
    log:notice('================ test logic_fw ================')
    test_logic_fw.main(bus)

    test_sr_prepare.main(bus)

    log:notice('================ test general_hardware complete ================')
end

skynet.start(function()
    clear_test_data(false)
    prepare_test_data()
    test_common.dbus_launch()
    skynet.uniqueservice('sd_bus')
    skynet.uniqueservice('persistence/service/main')
    skynet.uniqueservice('maca/service/main')
    skynet.uniqueservice('key_mgmt/service/main')
    skynet.uniqueservice('hwdiscovery/service/main')
    skynet.uniqueservice('ipmi_core/service/main')
    skynet.uniqueservice('fructrl/service/main')
    skynet.uniqueservice('frudata/service/main')
    skynet.uniqueservice('firmware_mgmt/service/main')
    skynet.uniqueservice('hwproxy/service/main')
    skynet.uniqueservice('compute/service/main')
    skynet.uniqueservice('main')
    skynet.sleep(3000) -- 自发现对象分发速度变慢，容易导致集成测试开始跑的时候对象还没分发完
    skynet.fork(function()
        local ok, err = pcall(main)
        skynet.timeout(100, function()
            clear_test_data(true)
        end)
        if not ok then
            error(err)
        end
    end)
end)
