-- 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 bios_service = require 'service.bios_service'
local bios_enum = require 'bios.types.enums'
local bs_util = require 'util.base_util'
local prop_def = require 'macros.property_def'
local bs = require 'mc.bitstring'
local file_object = require 'pojo.file_object'
local lu = require 'luaunit'
local open_db = require 'bios.db'
local utils = require 'mc.utils'
local client = require 'bios.client'
local project_dir = os.getenv('PROJECT_DIR')

local test_common = require 'test_common.utils'
local bus = test_common.dbus_launch('../.dbus', nil, true)

TestReadFromBmc = {}

local function construct_db()
    local ok, datas = pcall(require, 'bios.datas')
    if not ok then
        -- 如果没有datas配置，证明当前组件不需要datas，仅打开数据库
        datas = nil
    end
    local db = open_db(':memory:', datas)
    return db
end

function TestReadFromBmc:test_bean()
    utils.mkdir_with_parents(prop_def.BIOS_CONFIG_PATH .. '/' .. 1,
        utils.S_IRWXU | utils.S_IRGRP | utils.S_IXGRP)

    local bios_ser = bios_service.get_instance(bus, construct_db())
    bios_ser:add_object({
        get_system_id = function()
            return 1
        end,
        register_mdb_objects = function ()
        end,
        unregister_mdb_objects = function ()
        end,
        PcieCardBDF = '',
        PcieDiskBDF = '',
        OCPCardBDF = '',
        Slot = 1
    })
    bios_ser:set_bios_prop('CurrentValueFileName', project_dir .. '/test/test_data/currentvalue.json')
    bios_ser:set_bios_prop('ResultFileName', project_dir .. '/test/test_data/result.json')
    bios_ser:set_bios_prop('SettingFileName', project_dir .. '/test/test_data/setting.json')
    bios_ser:set_bios_prop('RegistryFileName', project_dir .. '/test/test_data/registry.json')
end

function TestReadFromBmc:test_z_clear()
    os.execute('rm -rf /data/opt/bmc/conf/bios/1')

    local bios_ser = bios_service.get_instance(bus, construct_db())
    bios_ser:add_object({
        get_system_id = function()
            return 1
        end,
        register_mdb_objects = function ()
        end,
        unregister_mdb_objects = function ()
        end,
        PcieCardBDF = '',
        PcieDiskBDF = '',
        OCPCardBDF = '',
        Slot = 1
    })
    bios_ser:set_bios_prop('CurrentValueFileName', '/data/opt/currentvalue.json')
    bios_ser:set_bios_prop('ResultFileName', '/data/opt/result.json')
    bios_ser:set_bios_prop('SettingFileName', '/data/opt/setting.json')
    bios_ser:set_bios_prop('RegistryFileName', '/data/opt/registry.json')
end

function TestReadFromBmc:test_get_pcie_silk_config()
    local pcie_obj_array, nic_obj_array = bios_service.get_pcie_silk_config()
    lu.assertEquals(#pcie_obj_array, 0)
    lu.assertEquals(#nic_obj_array, 0)
end

function TestReadFromBmc:test_prepare_silk_config()
    local data_opt = file_object.new()
    local bios_ser = bios_service.get_instance()
    local obj = bios_ser:get_obj(1)
    local cpu_table = {
        {LogicalId = 2, PhysicalId = 3}
    }
    local c_ForeachCPUObjects = client.ForeachCPUObjects
    client.ForeachCPUObjects = function(self, cb)
        for _, obj in pairs(cpu_table) do
            cb(obj)
        end
    end
    local res = bios_service.prepare_silk_config(data_opt, {}, {}, 1, obj, false)
    client.ForeachCPUObjects = c_ForeachCPUObjects
    lu.assertEquals(res, 1)
    lu.assertNotIsNil(data_opt:get_buf())
    lu.assertIsTrue(data_opt:get_buf() ~= '')
    lu.assertIsTrue(data_opt:get_len() ~= 0)
end

function TestReadFromBmc:test_get_check_sum()
    local test_string = 'ABCD'
    local check_sum = bs_util.calc_checksum(test_string)
    lu.assertEquals(check_sum, 0x0A)
end

function TestReadFromBmc:test_bios_read_file_from_bmc()
    local bios_ser = bios_service.get_instance()

    ---@diagnostic disable-next-line: duplicate-set-field
    function bios_ser:get_disk_silk_config()
        return {}
    end
    -- =================== test[1] ===================
    local resp = bios_ser:bios_read_file_from_bmc(nil)
    lu.assertNotIsNil(resp)
    lu.assertEquals(resp.CompletionCode, bios_enum.BiosErrCode.BIOS_ERR_INVALID_FIELD:value())

    -- =================== test[2] ===================
    local req = {
        ManufactureId = 0x0006DB,
        BiosId = 0x00,
        FileSelector = 0x2A,
        Operation = 0x00,
        Srcdata = ''
    }
    local resp = bios_ser:bios_read_file_from_bmc(req)
    lu.assertNotIsNil(resp)
    lu.assertEquals(resp.CompletionCode, bios_enum.BiosErrCode.BIOS_ERR_INVALID_CMD:value())

    -- =================== test[3] ===================
    local req = {
        ManufactureId = 0x0007DB,
        BiosId = 0x01,
        FileSelector = 0x2A,
        Operation = 0x00,
        Srcdata = ''
    }
    local resp = bios_ser:bios_read_file_from_bmc(req)
    lu.assertNotIsNil(resp)
    lu.assertEquals(resp.CompletionCode, bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value())
end

function TestReadFromBmc:test_bios_read_prepare_silkconfig()
    local bios_ser = bios_service.get_instance()
    -- =================== test[1] ===================
    local req = {
        ManufactureId = 0x0007DB,
        BiosId = 0x00,
        FileSelector = 0xFF,
        Operation = 0x00,
        Srcdata = ''
    }
    local obj = bios_ser:get_obj(1)
    local resp = bios_ser:bios_read_prepare(req, {}, obj)
    lu.assertNotIsNil(resp)
    lu.assertEquals(resp.CompletionCode, bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value())

    -- =================== test[2] ===================
    req = {
        ManufactureId = 0x0007DB,
        BiosId = 0x00,
        FileSelector = 0x2A,
        Operation = 0x00,
        Srcdata = ''
    }
    resp = bios_ser:bios_read_prepare(req, {}, obj)
    lu.assertNotIsNil(resp)
    lu.assertEquals(resp.CompletionCode, bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value())
    lu.assertEquals(resp.ManufactureId, 0x0007DB)
    lu.assertIsTrue(resp.FileBuffer ~= '')
    lu.assertEquals(#resp.FileBuffer, 5)
end

function TestReadFromBmc:test_bios_read_prepare_json()
    local read_file_ipmi_req = {
        ManufactureId = 0x0007DB,
        BiosId = 0x00,
        FileSelector = 0x00,
        Operation = 0x00,
        Srcdata = ''
    }
    local bios_ser = bios_service.get_instance()
    local obj = bios_ser:get_obj(1)
    -- =================== test[registry.json] ===================
    read_file_ipmi_req.FileSelector = 0x19
    local resp = bios_ser:bios_read_prepare(read_file_ipmi_req, {}, obj)
    lu.assertNotIsNil(resp)
    lu.assertEquals(resp.CompletionCode, bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value())
    lu.assertEquals(resp.ManufactureId, 0x0007DB)
    lu.assertIsTrue(resp.FileBuffer ~= '')
    lu.assertEquals(#resp.FileBuffer, 5)
    -- =================== test[currentvalue.json] ===================
    read_file_ipmi_req.FileSelector = 0x1A -- currentvalue.json关键字
    resp = bios_ser:bios_read_prepare(read_file_ipmi_req, {}, obj)
    lu.assertNotIsNil(resp)
    lu.assertEquals(resp.CompletionCode, bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value())
    lu.assertEquals(resp.ManufactureId, 0x0007DB)
    lu.assertIsTrue(resp.FileBuffer ~= '')
    lu.assertEquals(#resp.FileBuffer, 5)
    -- =================== test[setting.json] ===================
    read_file_ipmi_req.FileSelector = 0x1B -- setting.json关键字
    resp = bios_ser:bios_read_prepare(read_file_ipmi_req, {}, obj)
    lu.assertNotIsNil(resp)
    lu.assertEquals(resp.CompletionCode, bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value())
    lu.assertEquals(resp.ManufactureId, 0x0007DB)
    lu.assertIsTrue(resp.FileBuffer ~= '')
    lu.assertEquals(#resp.FileBuffer, 5)
    -- =================== test[result.json] ===================
    read_file_ipmi_req.FileSelector = 0x1C -- result.json关键字
    resp = bios_ser:bios_read_prepare(read_file_ipmi_req, {}, obj)
    lu.assertNotIsNil(resp)
    lu.assertEquals(resp.CompletionCode, bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value())
    lu.assertEquals(resp.ManufactureId, 0x0007DB)
    lu.assertIsTrue(resp.FileBuffer ~= '')
    lu.assertEquals(#resp.FileBuffer, 5)
end

local function get_srcdata(offset, len)
    local data = {OffsetFromRead = offset, ReadLen = len}
    local format = bs.new('<<OffsetFromRead:4/unit:8, ReadLen>>')
    return format:pack(data)
end

function TestReadFromBmc:test_bios_read_data()
    local bios_ser = bios_service.get_instance()
    local obj = bios_ser:get_obj(1)
    local req = {
        ManufactureId = 0x0007DB,
        BiosId = 0x00,
        FileSelector = 0x2A,
        Operation = 0x00,
        Srcdata = ''
    }
    ---@diagnostic disable-next-line: duplicate-set-field
    bios_ser.get_disk_silk_config = function ()
        return {}
    end
    bios_ser:bios_read_prepare(req, {}, obj)
    -- =================== test[1] ===================
    local resp = bios_ser:bios_read_data(nil)
    lu.assertNotIsNil(resp)
    assert(resp.CompletionCode == bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value())

    -- =================== test[2] ===================
    req = {
        ManufactureId = 0x0007DB,
        BiosId = 0x00,
        FileSelector = 0x2A,
        Operation = 0x00,
        Srcdata = get_srcdata(0x00000000, 12)
    }
    resp = bios_ser:bios_read_data(req)
    lu.assertNotIsNil(resp)
    lu.assertEquals(resp.CompletionCode, bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value())
    lu.assertEquals(resp.ManufactureId, 0x0007DB)
    lu.assertIsTrue(resp.FileBuffer ~= '')
    local check_sum = string.byte(string.sub(resp.FileBuffer, -1, -1))
    local data_string = string.sub(resp.FileBuffer, 2, -2)
    lu.assertEquals(bs_util.calc_checksum(data_string), check_sum)
end

function TestReadFromBmc:test_bios_read_finish()
    local bios_ser = bios_service.get_instance()
    -- =================== test[1] ===================
    local resp = bios_ser:bios_read_finish(nil)
    lu.assertNotIsNil(resp)
    lu.assertEquals(resp.CompletionCode, bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value())
    -- =================== test[2] ===================
    local req = {
        ManufactureId = 0x0007DB,
        BiosId = 0x00,
        FileSelector = 0x2A,
        Operation = 0x03,
        Srcdata = ''
    }
    local obj = bios_ser:get_obj(1)
    resp = bios_ser:bios_read_finish(req, {}, obj)
    lu.assertNotIsNil(resp)
    lu.assertEquals(resp.CompletionCode, bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value())
    lu.assertEquals(resp.ManufactureId, 0x0007DB)
    lu.assertIsTrue(resp.FileBuffer ~= '')
    local reserved = string.byte(resp.FileBuffer, 1, 1)
    lu.assertEquals(reserved, prop_def.BIOS_FILE_NO_MORE_DATA)
end

function TestReadFromBmc:test_get_rd_prepare_resp()
    local resp = bios_service.get_rd_prepare_resp(0x12345678)
    local format = bs.new('<<Reserved, DataLen:4/unit:8>>')
    local req = format:unpack(resp)
    lu.assertNotIsNil(req)
    lu.assertEquals(req.Reserved, prop_def.BIOS_FILE_MORE_DATA)
    lu.assertEquals(req.DataLen, 0x12345678)
end

function TestReadFromBmc:test_get_rd_finish_resp()
    local resp = bios_service.get_rd_finish_resp(0xFF)
    local format = bs.new('<<Reserved, CheckSum>>')
    local req = format:unpack(resp)
    lu.assertNotIsNil(req)
    lu.assertEquals(req.Reserved, prop_def.BIOS_FILE_NO_MORE_DATA)
    lu.assertEquals(req.CheckSum, 0xFF)
end
