-- 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 bs = require 'mc.bitstring'
local lu = require 'luaunit'
local hardware_config = require 'nvme.nvme_mi'
local dump = require 'nvme.dump_nvme_log'
local NVME_MI_OPTIONS = require 'nvme.nvme_mi_def'
local file_sec = require 'utils.file'
local utils = require 'mc.utils'
local project_dir = os.getenv('PROJECT_DIR')

TEST_test_nvme_mctp = {}

local function pack(obj)
    local result = ''
    for _, v in ipairs(obj) do
        result = result .. v
    end
    return result
end

local expect_called_and_return = {
    {
        mock_response = pack({
            '\x20',     -- status
            '\x01',     -- NVM Subsystem Status (NSS)
            '\x00',     -- SMART Warnings (SW)
            '\x30',     -- Composite Temperature (CTEMP)
            '\x1e',     -- Percentage Drive Life Used (PDLU)
            '\x1e\xa1', -- Composite Controller Status (CCS)
            '\x00\x00', -- Reserved
        }),
        expect = {
            status = 0x20,
            nss = 0x01,
            sw = 0x00,
            ctemp = 0x30,
            pdlu = 0x1e,
            ccs = 0xA11E,
            reserved = 0x00,
        }
    }
}

function TEST_test_nvme_mctp:test_nvme_mctp_get_subsystemstatus()
    local response = function(data)
        local r = bs.new([[<<
            status:8,
            nss:8,
            sw:8,
            ctemp:8,
            pdlu:8,
            ccs:16,
            re:16
        >>]]):unpack(data)
        return {
            status = r.status,
            nss = r.nss,
            sw = r.sw,
            ctemp = r.ctemp,
            pdlu = r.pdlu,
            ccs = r.ccs,
            reserved = r.re
        }
    end
    for _, tab in pairs(expect_called_and_return) do
        local rsp = response(tab.mock_response)
        lu.assertEquals(rsp.status, tab.expect.status)
        lu.assertEquals(rsp.nss, tab.expect.nss)
        lu.assertEquals(rsp.sw, tab.expect.sw)
        lu.assertEquals(rsp.ctemp, tab.expect.ctemp)
        lu.assertEquals(rsp.ccs, tab.expect.ccs)
        lu.assertEquals(rsp.reserved, tab.expect.reserved)
    end
end

local smart_log_expect_request = {
    message_type = NVME_MI_OPTIONS.NVME_ADMIN_COMMANDS_OPCODE.NVME_ADMIN_GET_LOG_PAGE,
    command_slot_identifier = 0,
    data = pack({
        '\x02',             -- opcode
        '\x03',             -- flags
        '\x01\x00',         -- ctrl_id
        '\xFF\xFF\xFF\xFF', -- nsid
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- data_offset
        '\x00\x02\x00\x00', -- data_len
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x02\x00\x7F\x00', -- cdw10
        '\x00\x00\x00\x00', -- cdw11
        '\x00\x00\x00\x00', -- cdw12
        '\x00\x00\x00\x00', -- cdw13
        '\x00\x00\x00\x00', -- cdw14
        '\x00\x00\x00\x00'  -- rsvd
    })
}

function TEST_test_nvme_mctp:test_nvme_mctp_smart_log_request()
    local request = hardware_config.mctp().properties.SmartLog.request
    for k, v in pairs(smart_log_expect_request) do
        lu.assertEquals(request[k], v)
    end
end

local smart_log_expect_response = {
    host_reads               = 69889,
    critical_warning         = 0,
    endu_grp_crit_warn_sumry = 0,
    critical_comp_time       = 0,
    avail_spare              = 100,
    thm_temp1_trans_count    = 0,
    power_on_hours           = 17,
    unsafe_shutdowns         = 1,
    temperature              = 307,
    warning_temp_time        = 2,
    ctrl_busy_time           = 0,
    power_cycles             = 52,
    data_units_read          = 2918,
    percent_used             = 1,
    thm_temp1_total_time     = 0,
    thm_temp2_total_time     = 308,
    thm_temp2_trans_count    = 0,
    num_err_log_entries      = 0,
    media_errors             = 0,
    temp_sensor1             = 0,
    temp_sensor2             = 0,
    temp_sensor3             = 0,
    temp_sensor4             = 0,
    temp_sensor5             = 0,
    temp_sensor6             = 0,
    temp_sensor7             = 0,
    temp_sensor8             = 0,
    host_writes              = 3286,
    data_units_written       = 51,
    spare_thresh             = 10
}

function TEST_test_nvme_mctp:test_nvme_mctp_smart_log_response()
    local response = hardware_config.mctp().properties.SmartLog.response(pack({
        '\x00\x00\x00',
        '\x00\x00\x00\x00',
        '\x00\x00\x00\x00',
        '\x00\x00\x00\x00',
        '\x00',                                                             -- critical_warning
        '\x33\x01',                                                         -- temperature
        '\x64',                                                             -- avail_spare
        '\x0A',                                                             -- spare_thresh
        '\x01',                                                             -- percent_used
        '\x00',                                                             -- endu_grp_crit_warn_sumry
        '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
        '\x66\x0B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', -- data_units_read
        '\x33\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', -- data_units_written
        '\x01\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', -- data_units_written
        '\xD6\x0C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', -- host_reads
        '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', -- host_writes
        '\x34\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', -- ctrl_busy_time
        '\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', -- power_cycles
        '\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', -- power_on_hours
        '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', -- unsafe_shutdowns
        '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', -- media_errors
        '\x02\x00\x00\x00',                                                 -- warning_temp_time
        '\x00\x00\x00\x00',                                                 -- critical_comp_time
        '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', -- temp_sensor
        '\x00\x00\x00\x00',                                                 -- thm_temp1_trans_count
        '\x00\x00\x00\x00',                                                 -- thm_temp2_trans_count
        '\x00\x00\x00\x00',                                                 -- thm_temp1_total_time
        '\x34\x01\x00\x00',                                                 -- thm_temp2_total_time
        '\x00\x00\x00\x00'                                                  -- rsvd
    }))
    for k, v in pairs(smart_log_expect_response) do
        lu.assertEquals(response[k], v)
    end
end

local fw_log_expect_request = {
    message_type = NVME_MI_OPTIONS.NVME_ADMIN_COMMANDS_OPCODE.NVME_ADMIN_GET_LOG_PAGE,
    command_slot_identifier = 0,
    data = pack({
        '\x02',             -- opcode
        '\x03',             -- flags
        '\x01\x00',         -- ctrl_id
        '\xFF\xFF\xFF\xFF', -- nsid
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- data_offset
        '\x00\x02\x00\x00', -- data_len
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x03\x80\x7F\x00', -- cdw10
        '\x00\x00\x00\x00', -- cdw11
        '\x00\x00\x00\x00', -- cdw12
        '\x00\x00\x00\x00', -- cdw13
        '\x00\x00\x00\x00', -- cdw14
        '\x00\x00\x00\x00'  -- rsvd
    })
}

function TEST_test_nvme_mctp:test_nvme_mctp_fw_log_request()
    local request = hardware_config.mctp().properties.FwLog.request
    for k, v in pairs(fw_log_expect_request) do
        lu.assertEquals(request[k], v)
    end
end

local fw_log_expect_response = {
    afi = 1,
    frs1 = 2500,
    frs2 = 1549,
    frs6 = 1512
}

function TEST_test_nvme_mctp:test_nvme_mctp_fw_log_response()
    local response = hardware_config.mctp().properties.FwLog.response(pack({
        '\x00\x00\x00',
        '\x00\x00\x00\x00',
        '\x00\x00\x00\x00',
        '\x00\x00\x00\x00',
        '\x01',                             -- afi
        '\x00\x00\x00\x00\x00\x00\x00',     -- rsvd
        '\x32\x35\x30\x30\x20\x20\x20\x20', -- frs1
        '\x31\x35\x34\x39\x20\x20\x20\x20', -- frs2
        '\x00\x00\x00\x00\x00\x00\x00\x00', -- frs3
        '\x00\x00\x00\x00\x00\x00\x00\x00', -- frs4
        '\x00\x00\x00\x00\x00\x00\x00\x00', -- frs5
        '\x31\x35\x31\x32\x20\x20\x20\x20', -- frs6
        '\x00\x00\x00\x00\x00\x00\x00\x00', -- frs7
        '\x00\x00\x00\x00\x00\x00\x00\x00', -- rsvd
    }))
    for k, v in pairs(fw_log_expect_response) do
        lu.assertEquals(response[k], v)
    end
end

local error_log_expect_request = {
    message_type = NVME_MI_OPTIONS.NVME_ADMIN_COMMANDS_OPCODE.NVME_ADMIN_GET_LOG_PAGE,
    command_slot_identifier = 0,
    data = pack({
        '\x02',             -- opcode
        '\x03',             -- flags
        '\x01\x00',         -- ctrl_id
        '\xFF\xFF\xFF\xFF', -- nsid
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- data_offset
        '\x00\x10\x00\x00', -- data_len
        '\x00\x00\x00\x00', -- rsvd
        '\x00\x00\x00\x00', -- rsvd
        '\x01\x00\xFF\x03', -- cdw10
        '\x00\x00\x00\x00', -- cdw11
        '\x00\x00\x00\x00', -- cdw12
        '\x00\x00\x00\x00', -- cdw13
        '\x00\x00\x00\x00', -- cdw14
        '\x00\x00\x00\x00'  -- rsvd
    })
}

function TEST_test_nvme_mctp:test_nvme_mctp_error_log_request()
    local request = hardware_config.mctp().properties.ErrorInformation.request
    for k, v in pairs(error_log_expect_request) do
        lu.assertEquals(request[k], v)
    end
end

local error_log_expect_response = {
    [1] = {
        error_count = 0,
        sqid = 0,
        cmdid = 0,
        status_field = 0,
        parm_error_location = 0,
        lba = 0,
        nsid = 0,
        vsia = 0,
        trtype = 0,
        csi = 0,
        trtype_spec_info = 0
    }
}

function TEST_test_nvme_mctp:test_nvme_mctp_error_log_response()
    local response = hardware_config.mctp().properties.ErrorInformation.response(pack({
        '\x00\x00\x00',
        '\x00\x00\x00\x00',
        '\x00\x00\x00\x00',
        '\x00\x00\x00\x00',
        '\x00\x00\x00\x00\x00\x00\x00\x00', -- error_count
        '\x00\x00',                         -- sqid
        '\x00\x00',                         -- cmdid
        '\x00\x00',                         -- status_field
        '\x00\x00',                         -- parm_error_location
        '\x00\x00\x00\x00\x00\x00\x00\x00', -- lba
        '\x00\x00\x00\x00',                 -- nsid
        '\x00',                             -- vsia
        '\x00',                             -- trtype
        '\x00\x00',                         -- rsvd1
        '\x00\x00\x00\x00\x00\x00\x00\x00', -- csi
        '\x00\x00',                         -- trtype_spec_info
        '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
    }))
    for k, v in pairs(error_log_expect_response[1]) do
        lu.assertEquals(response[1][k], v)
    end
end

-- 测试dump日志
function TEST_test_nvme_mctp:test_nvme_dump_info()
    local obj_info_path = project_dir .. '/test/unit/nvme.log'
    print(obj_info_path)
    local fp_w = file_sec.open_s(obj_info_path, 'w+')
    lu.assertNotIsNil(fp_w)

    local obj = {
        smart_log = smart_log_expect_response,
        feature_identifiers = {
            [1] = 1
        },
        fw_log = {
            afi = 1,
            frs1 = tonumber('2500'),
            frs2 = tonumber('   ')
        },
        supported_log_pages = {
            [2] = 1
        },
        error_log = error_log_expect_response,
        controller_health_status = {},
        mctp_trans_unit_size = {},
        nvm_subsystem_info = {},
        subsys_health_status = {}
    }
    dump.dump_log(obj, fp_w)
    fp_w:close()

    local file = file_sec.open_s(obj_info_path, "r")
    lu.assertNotIsNil(file)
    local content = utils.close(file, pcall(file.read, file, "*a"))
    lu.assertNotIsNil(content)
    -- 检查信息
    lu.assertNotEquals(string.find(content, 'available_spare_threshold'), nil)
    lu.assertEquals(string.find(content, 'temp_sensor4'), nil)
    lu.assertNotEquals(string.find(content, 'Error Log'), nil)
    lu.assertNotEquals(string.find(content, 'The command completed successfully'), nil)
    lu.assertNotEquals(string.find(content, 'trtype_spec_info: 0'), nil)
    lu.assertNotEquals(string.find(content, 'NAMESPACE SCOPE-'), nil)
    lu.assertNotEquals(string.find(content, 'frs1 : 2500'), nil)

    utils.remove_file(obj_info_path)
end