-- 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 smbios_service = require 'service.smbios_service'
local bios_service = require 'service.bios_service'
local bios_enum = require 'bios.types.enums'
local bios_factory = require 'factory.bios_factory'
local bs_util = require 'util.base_util'
local prop_def = require 'macros.property_def'
local bs = require 'mc.bitstring'
local pro_global = require 'macros.property_global'
local struct = require 'pojo.struct_def'
local open_db = require 'bios.db'

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

TestBiosWriteData = {}

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 TestBiosWriteData:test_bean()
    local smbios_ser = smbios_service.get_instance()
    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_factory.register_bean('bios_service', bios_ser)
    bios_factory.register_bean('smbios_service', smbios_ser)

    bios_ser:set_bios_prop('CurrentValueFileName', '../test_data/currentvalue1.json')
    bios_ser:set_bios_prop('ResultFileName', '../test_data/result1.json')
    bios_ser:set_bios_prop('SettingFileName', '../test_data/setting1.json')
    bios_ser:set_bios_prop('RegistryFileName', '../test_data/registry1.json')
    bios_ser:set_bios_prop('CMESFileName', '../test_data/CMES1.dat')
    bios_ser:set_bios_prop('SecureBootCurrentFile', '../test_data/current_secureboot1.json')
    assert(smbios_ser ~= nil)
    assert(bios_ser ~= nil)
end

function TestBiosWriteData:test_z_clear()
    local bios_ser = bios_service.get_instance(bus, construct_db())

    os.execute('rm -rf ../test_data/currentvalue1.json')
    os.execute('rm -rf ../test_data/result1.json')
    os.execute('rm -rf ../test_data/setting1.json')
    os.execute('rm -rf ../test_data/registry1.json')
    os.execute('rm -rf ../test_data/CMES1.dat')
    os.execute('rm -rf ../test_data/current_secureboot1.json')

    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')
    bios_ser:set_bios_prop('CMESFileName', '/data/opt/bmc/conf/bios/CMES.dat')
    bios_ser:set_bios_prop('SecureBootCurrentFile',
        '/data/opt/bmc/conf/bios/current_secureboot.json')
end

local function construct_ctx()
    local ctx = {}
    ctx.ChanType = 1
    ctx.get_initiator = function()
        return {}
    end

    return ctx
end

function TestBiosWriteData:test_judge_req()
    local req = nil
    local data = bs_util.judge_req(req)

    assert(data ~= nil)
    assert(data == bios_enum.BiosErrCode.BIOS_ERR_INVALID_FIELD:value())

    req = {ManufactureId = 0}
    data = bs_util.judge_req(req)
    assert(data ~= nil)
    assert(data == bios_enum.BiosErrCode.BIOS_ERR_IV_LEN:value())

    req = {ManufactureId = 1, BiosId = 2, FileSelector = 1, Operation = 3}
    data = bs_util.judge_req(req)
    assert(data == nil)
end

function TestBiosWriteData:test_judge_manu_id_valid()
    local req = {ManufactureId = 0, BiosId = 2, FileSelector = 1, Operation = 3}

    local err_code = bs_util.judge_manu_id_valid(req, nil)
    assert(err_code == -1)

    err_code = bs_util.judge_manu_id_valid(req, 1)
    assert(err_code == -1)

    err_code = bs_util.judge_manu_id_valid(req, 0)
    assert(err_code == 1)
end

function TestBiosWriteData:test_judge_bios_id()
    local req = {ManufactureId = 0, BiosId = 1, FileSelector = 1, Operation = 3}

    local data = bs_util.judge_bios_id(req, 0)
    assert(data ~= nil)
    assert(data == bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value())

    data = bs_util.judge_bios_id(req, 2)
    assert(data == nil)
end

function TestBiosWriteData:test_validate()
    local req = {ManufactureId = 0xDB0700, BiosId = 0, FileSelector = 1, Operation = 3}
    local data = bs_util.validate(req, 0, 0xDB0700)
    assert(data == nil)

    req = nil
    data = bs_util.validate(req, 0, 0)
    assert(data ~= nil)
    assert(data == bios_enum.BiosErrCode.BIOS_ERR_INVALID_FIELD:value())

    req = {ManufactureId = 0, BiosId = 1, FileSelector = 1, Operation = 3}
    data = bs_util.validate(req, 0, 0)
    assert(data ~= nil)
    assert(data == bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value())

    req = {ManufactureId = 0, BiosId = 0, FileSelector = 1, Operation = 3}
    data = bs_util.validate(req, 0, 1)
    assert(data ~= nil)
    assert(data == bios_enum.BiosErrCode.BIOS_ERR_INVALID_CMD:value())
end

function TestBiosWriteData:test_resolve_data()
    local manu_id = 0x0007DB
    local data = bs_util.resolve_data(3, manu_id)
    assert(data == 0xDB0700)

    manu_id = 0x31313131
    data = bs_util.resolve_data(4, manu_id)
    assert(data == 0x31313131)
end

function TestBiosWriteData:test_get_bios_wr_data()
    local src_data = '11111111111111111'
    local data = bs_util.get_bios_wr_data(src_data)
    assert(data ~= nil)
    assert(data.DataChecksum == 49)
    assert(data.OffsetToWrite == 825307441)
    assert(data.FileData == '111111111111')
end

function TestBiosWriteData:test_clear_data_operate_res()
    bs_util.clear_data_operate_res(nil)

    local data_opt = {
        data_flag = prop_def.BIOS_FLAG_IDLE,
        data_offset = 0x00,
        data_len = 0x00,
        data_buf = nil,
        file_size_limit = prop_def.BIOS_FILE_MAX_SIZE
    }
    bs_util.clear_data_operate_res(data_opt)
end

function TestBiosWriteData:test_judge_write_file_selector_valid()
    local res = bs_util.judge_write_file_selector_valid(prop_def.BIOS_FILE_DISPLAY_NUM)
    assert(res == true)

    res = bs_util.judge_write_file_selector_valid(prop_def.BIOS_FILE_DEBUG_INFO)
    assert(res == false)

    res = bs_util.judge_write_file_selector_valid(0x9e)
    assert(res == false)
end

function TestBiosWriteData:test_get_file_json()
    local data = bs_util.get_file_json(nil)
    assert(data == nil)

    data = bs_util.get_file_json('/opt/currentValue.json')
    assert(data == nil)
end

function TestBiosWriteData:test_default_write()
    local bios_ser = bios_service.get_instance(bus, {})

    local data = bios_ser:default_write()
    assert(data ~= nil)
    assert(data.CompletionCode == bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value())
    assert(data.ManufactureId == 0x0007DB)
    assert(data.RequiredOffset == '')
end

function TestBiosWriteData:test_get_manu_id()
    local bios_ser = bios_service.get_instance(bus, {})

    local data = bios_ser:get_manu_id()
    assert(data ~= nil)
    assert(data == 0x0007DB)
end

function TestBiosWriteData:test_get_bios_id()
    local bios_ser = bios_service.get_instance(bus, {})

    local data = bios_ser:get_bios_id()
    assert(data ~= nil)
    assert(data == 0)
end

function TestBiosWriteData:test_get_data_opt()
    local bios_ser = bios_service.get_instance(bus, {})
    local data_opt, file_size_limit = bios_ser:get_data_opt(prop_def.BIOS_FILE_FIRMWARE, 1)
    assert(data_opt ~= nil)
    assert(file_size_limit == prop_def.MAX_FILE_SIZE_90M)

    data_opt, file_size_limit = bios_ser:get_data_opt(0, 1)
    assert(data_opt ~= nil)
    assert(file_size_limit == prop_def.BIOS_FILE_MAX_SIZE)
end

local function get_obj()
    return {
        check_config = function()
            return true
        end
    }
end

function TestBiosWriteData:test_bios_write_prepare()
    local bios_ser = bios_service.get_instance(bus, {}, obj)
    local req = nil

    local data = bios_ser:bios_write_prepare(req, {}, get_obj())
    assert(data ~= nil)
    assert(data.CompletionCode == bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value())
    assert(data.ManufactureId == 0x0007DB)
    assert(data.RequiredOffset == '')

    local format = bs.new('<<DataChecksum, OffsetToWrite:4/unit:8, FileData/string>>')
    local tail_data = {DataChecksum = 0, OffsetToWrite = 0x11111111, FileData = ''}

    local src_data = format:pack(tail_data)
    req = {ManufactureId = 0, BiosId = 0, FileSelector = 1, Operation = 3, Srcdata = src_data}
    data = bios_ser:bios_write_prepare(req, nil, get_obj())
    assert(data ~= nil)
    assert(data.CompletionCode == bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value())
    assert(data.ManufactureId == 0x0007DB)
    assert(data.RequiredOffset == '')

    tail_data = {DataChecksum = 0, OffsetToWrite = 0x00000000, FileData = ''}

    src_data = format:pack(tail_data)
    req = {ManufactureId = 0, BiosId = 0, FileSelector = 1, Operation = 3, Srcdata = src_data}
    data = bios_ser:bios_write_prepare(req, construct_ctx(), get_obj())
    assert(data ~= nil)
    assert(data.CompletionCode == bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value())
    assert(data.ManufactureId == 0x0007DB)
    assert(data.RequiredOffset == '')
end

function TestBiosWriteData:test_bios_write_data()
    local bios_ser = bios_service.get_instance(bus, {})
    local data = bios_ser:bios_write_data(nil)
    assert(data ~= nil)
    assert(data.CompletionCode == bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value())
    assert(data.ManufactureId == 0x0007DB)
    assert(data.RequiredOffset == '')

    local req = {ManufactureId = 0, BiosId = 0, FileSelector = 1, Operation = 3}
    data = bios_ser:bios_write_data(req)
    assert(data ~= nil)
    assert(data.CompletionCode == bios_enum.BiosErrCode.BIOS_ERR_IV_LEN:value())
    assert(data.ManufactureId == 0x0007DB)
    assert(data.RequiredOffset == '')

    local format = bs.new('<<DataChecksum, OffsetToWrite:4/unit:8, FileData/string>>')
    local tail_data = {DataChecksum = 0, OffsetToWrite = 0x00010000, FileData = ''}
    local src_data = format:pack(tail_data)
    req = {ManufactureId = 0, BiosId = 0, FileSelector = 1, Operation = 3, Srcdata = src_data}
    bios_ser:bios_write_prepare(req, construct_ctx(), get_obj())
    data = bios_ser:bios_write_data(req)
    assert(data ~= nil)
    assert(data.CompletionCode == bios_enum.BiosErrCode.BIOS_ERR_IV_LEN:value())
    assert(data.ManufactureId == 0x0007DB)
    assert(data.RequiredOffset == '')

    tail_data = {DataChecksum = 0, OffsetToWrite = 0x01000000, FileData = '123456'}
    src_data = format:pack(tail_data)
    req = {ManufactureId = 0, BiosId = 0, FileSelector = 1, Operation = 3, Srcdata = src_data}
    data = bios_ser:bios_write_data(req)
    assert(data ~= nil)
    assert(data.CompletionCode == bios_enum.BiosErrCode.BIOS_ERR_WRONG_OFFSET:value())
    assert(data.ManufactureId == 0x0007DB)
    assert(data.RequiredOffset == '0')

    tail_data = {DataChecksum = 0, OffsetToWrite = 0x00000000, FileData = '123456'}
    src_data = format:pack(tail_data)
    req = {ManufactureId = 0, BiosId = 0, FileSelector = 1, Operation = 3, Srcdata = src_data}
    data = bios_ser:bios_write_data(req)
    assert(data ~= nil)
    assert(data.CompletionCode == bios_enum.BiosErrCode.BIOS_ERR_INVALID_CHKSUM:value())
    assert(data.ManufactureId == 0x0007DB)
    assert(data.RequiredOffset == '')

    tail_data = {DataChecksum = 53, OffsetToWrite = 0x00000000, FileData = '123456'}
    src_data = format:pack(tail_data)
    req = {ManufactureId = 0, BiosId = 0, FileSelector = 1, Operation = 3, Srcdata = src_data}
    data = bios_ser:bios_write_data(req)
    assert(data ~= nil)
    assert(data.CompletionCode == bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value())
    assert(data.ManufactureId == 0x0007DB)
    assert(data.RequiredOffset == '')
end

local function send_prepare_data(data, chk_sum)
    local format = bs.new('<<DataChecksum, OffsetToWrite:4/unit:8, FileData/string>>')
    local bios_ser = bios_service.get_instance(bus, {})
    local tail_data = {DataChecksum = 0, OffsetToWrite = 0x0001000, FileData = '123456'}
    local src_data = format:pack(tail_data)
    local req = {
        ManufactureId = 0,
        BiosId = 0,
        FileSelector = 0x1b,
        Operation = 3,
        Srcdata = src_data
    }
    bios_ser:bios_write_prepare(req, construct_ctx(), get_obj())
    tail_data = {DataChecksum = chk_sum, OffsetToWrite = 0x0000000, FileData = data}
    src_data = format:pack(tail_data)
    req = {ManufactureId = 0, BiosId = 0, FileSelector = 0x1b, Operation = 3, Srcdata = src_data}
    bios_ser:bios_write_data(req)
end

function TestBiosWriteData:test_bios_write_finish()
    pro_global.G_BIOS_FILE_CHANGE[0] = struct.bios_file_Change.new({})

    local bios_ser = bios_service.get_instance(bus, {})
    local req = {ManufactureId = 0, BiosId = 0, FileSelector = 0x9c, Operation = 0, Srcdata = ''}
    local data = bios_ser:bios_write_finish(req)
    assert(data ~= nil)
    assert(data.CompletionCode == bios_enum.BiosErrCode.BIOS_ERR_IV_LEN:value())
    assert(data.ManufactureId == 0x0007DB)
    assert(data.RequiredOffset == '')

    local format = bs.new('<<DataChecksum, OffsetToWrite:4/unit:8, FileData/string>>')
    local tail_data = {DataChecksum = 0, OffsetToWrite = 0x00000000, FileData = ''}
    local src_data = format:pack(tail_data)
    req = {ManufactureId = 0, BiosId = 0, FileSelector = 0x1b, Operation = 3, Srcdata = src_data}
    data = bios_ser:bios_write_finish(req)
    assert(data ~= nil)
    assert(data.CompletionCode == bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value())
    assert(data.ManufactureId == 0x0007DB)
    assert(data.RequiredOffset == '')

    -- 测试chksum.json
    send_prepare_data('{ "Messages": [ ], "Time": "2044-01-13T10:56:46+00:00" }', 81)
    tail_data = {DataChecksum = 53, OffsetToWrite = 0x0001000, FileData = ''}
    src_data = format:pack(tail_data)
    req = {ManufactureId = 0, BiosId = 0, FileSelector = 0x1b, Operation = 3, Srcdata = src_data}
    data = bios_ser:bios_write_finish(req)
    assert(data ~= nil)
    assert(data.CompletionCode == bios_enum.BiosErrCode.BIOS_ERR_INVALID_CHKSUM:value())
    assert(data.ManufactureId == 0x0007DB)
    assert(data.RequiredOffset == '')

    -- 测试result.json
    send_prepare_data('{ "Messages": [ ], "Time": "2044-01-13T10:56:46+00:00" }', 175)
    tail_data = {DataChecksum = 175, OffsetToWrite = 0x0001000, FileData = ''}
    src_data = format:pack(tail_data)
    req = {
        ManufactureId = 0,
        BiosId = 0,
        FileSelector = prop_def.BIOS_FILE_RESULT_NUM,
        Operation = 3,
        Srcdata = src_data
    }
    local obj = bios_ser:get_obj(1)
    data = bios_ser:bios_write_finish(req, construct_ctx(), obj)
    assert(data ~= nil)
    assert(data.CompletionCode == bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value())
    self:bios_write_finish_continue(format, bios_ser, obj)
end

function TestBiosWriteData:bios_write_finish_continue(format, bios_ser, obj)
    -- 测试currentValue.json
    send_prepare_data(
        '{ "Messages": [ ], "Time": "2044-01-13T10:56:46+00:00", "PowerSaving": "Enabled"}', 221)
    local tail_data = {DataChecksum = 221, OffsetToWrite = 0x0001000, FileData = ''}
    local src_data = format:pack(tail_data)
    local req = {
        ManufactureId = 0,
        BiosId = 0,
        FileSelector = prop_def.BIOS_FILE_CURRENT_VALUE_NUM,
        Operation = 3,
        Srcdata = src_data
    }
    local data = bios_ser:bios_write_finish(req, construct_ctx(), obj)
    assert(data ~= nil)
    assert(data.CompletionCode == bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value())

    -- 测试registry.json
    send_prepare_data('{ "Messages": [ ], "Time": "2044-01-13T10:56:46+00:00" }', 175)
    tail_data = {DataChecksum = 175, OffsetToWrite = 0x0001000, FileData = ''}
    src_data = format:pack(tail_data)
    req = {
        ManufactureId = 0,
        BiosId = 0,
        FileSelector = prop_def.BIOS_FILE_REGISTRY_NUM,
        Operation = 3,
        Srcdata = src_data
    }
    data = bios_ser:bios_write_finish(req, construct_ctx(), obj)
    assert(data ~= nil)
    assert(data.CompletionCode == bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value())

    send_prepare_data('{ "Messages": [ ], "Time": "2044-01-13T10:56:46+00:00", "PowerSaving": 6}',
        36)
    tail_data = {DataChecksum = 36, OffsetToWrite = 0x0001000, FileData = ''}

    src_data = format:pack(tail_data)
    req = {
        ManufactureId = 0,
        BiosId = 0,
        FileSelector = prop_def.BIOS_FILE_CURRENT_VALUE_NUM,
        Operation = 3,
        Srcdata = src_data
    }
    data = bios_ser:bios_write_finish(req, construct_ctx(), obj)
    assert(data ~= nil)
    assert(data.CompletionCode == bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value())
    self:bios_write_continue_again(format, bios_ser, obj)
end

function TestBiosWriteData:bios_write_continue_again(format, bios_ser, obj)
    -- 测试currentValue.json
    send_prepare_data(
        '{ "Messages": [ ], "Time": "2044-01-13T10:56:46+00:00", "PowerSaving": "Disabled"}', 74)
    local tail_data = {DataChecksum = 74, OffsetToWrite = 0x0001000, FileData = ''}
    local src_data = format:pack(tail_data)
    local req = {
        ManufactureId = 0,
        BiosId = 0,
        FileSelector = prop_def.BIOS_FILE_CURRENT_VALUE_NUM,
        Operation = 3,
        Srcdata = src_data
    }
    local data = bios_ser:bios_write_finish(req, construct_ctx(), obj)
    assert(data ~= nil)
    assert(data.CompletionCode == bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value())

    -- 测试setting.json
    send_prepare_data(
        '{"BootTypeOrder0": "HardDiskDrive","DDRDebugLevel": "Minimum","DDRFreqLimit": "Auto"}', 241)
    tail_data = {DataChecksum = 241, OffsetToWrite = 0x0001000, FileData = ''}
    src_data = format:pack(tail_data)
    req = {
        ManufactureId = 0,
        BiosId = 0,
        FileSelector = prop_def.BIOS_FILE_SETTING_NUM,
        Operation = 3,
        Srcdata = src_data
    }
    data = bios_ser:bios_write_finish(req, construct_ctx(), obj)
    assert(data ~= nil)
    assert(data.CompletionCode == bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value())
end

-- -- 测试CMES.dat
function TestBiosWriteData:test_bios_write_finish_CMES()
    pro_global.G_BIOS_FILE_CHANGE[0] = struct.bios_file_Change.new({})

    local bios_ser = bios_service.get_instance(bus, {})
    local format = bs.new('<<DataChecksum, OffsetToWrite:4/unit:8, FileData/string>>')

    send_prepare_data('{ "Messages": [ ], "Time": "2044-01-13T10:56:46+00:00" }', 175)
    local tail_data = {DataChecksum = 175, OffsetToWrite = 0x0001000, FileData = ''}
    local src_data = format:pack(tail_data)
    local req = {
        ManufactureId = 0,
        BiosId = 0,
        FileSelector = prop_def.BIOS_FILE_CMES_NUM,
        Operation = 3,
        Srcdata = src_data
    }
    local obj = bios_ser:get_obj(1)
    local data = bios_ser:bios_write_finish(req, construct_ctx(), obj)
    assert(data ~= nil)
    assert(data.CompletionCode == bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value())
    assert(data.ManufactureId == 0x0007DB)
end

-- -- 测试 current_secureboot.json
function TestBiosWriteData:test_bios_write_finish_CURRENT_SECUREBOOT()
    pro_global.G_BIOS_FILE_CHANGE[0] = struct.bios_file_Change.new({})

    local bios_ser = bios_service.get_instance(bus, {})
    local format = bs.new('<<DataChecksum, OffsetToWrite:4/unit:8, FileData/string>>')

    send_prepare_data('{ "Messages": [ ], "Time": "2044-01-13T10:56:46+00:00" }', 175)
    local tail_data = {DataChecksum = 175, OffsetToWrite = 0x0001000, FileData = ''}
    local src_data = format:pack(tail_data)
    local req = {
        ManufactureId = 0,
        BiosId = 0,
        FileSelector = prop_def.BIOS_FILE_CURRENT_SECUREBOOT_NUM,
        Operation = 3,
        Srcdata = src_data
    }
    local obj = bios_ser:get_obj(1)
    local data = bios_ser:bios_write_finish(req, construct_ctx(), obj)
    assert(data ~= nil)
    assert(data.CompletionCode == bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value())
    assert(data.ManufactureId == 0x0007DB)
end
