-- 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_app = require 'bios_app'
local bios_factory = require 'factory.bios_factory'
local alarm_manager = require 'domain.alarm.alarm_manager'
local fructl_handler = require 'infrastructure.fructl'
local flash = require 'libmgmt_protocol.bios.domain.pfr.flash'
local defs = require 'libmgmt_protocol.bios.domain.pfr.defs'
local gold_package = require 'domain.bios_firmware.gold.gold_package'
local client = require 'bios.client'
local bios_service = require 'service.bios_service'
local lu = require 'luaunit'
local pfr_service = require 'service.pfr_service'
local power_lock = require 'infrastructure.power_lock'
local _, mtd_api = pcall(require, 'mtd.drv')

local SECURE_BOOT_PATH<const>   = '/bmc/kepler/Managers/1/SecureBoot'

test_flash = {}

local function construct_format(name, offset, size, type)
    return {
        ['name'] = name,
        ['offset'] = offset,
        ['size'] = size,
        ['type'] = type
    }
end

function test_flash:test_parse_format_data()
    local flash_instance = flash.new()
    local data = flash_instance:parse_format_data('\x01\x00\x00\x00\x56\x56\x56\x56',
        {
            construct_format('Magic', 0, 4, defs.NUM_TYPE),
            construct_format('Tag', 4, 8, defs.STRING_TYPE)
        }
    )
    lu.assertEquals(data.Magic, 1)
    lu.assertEquals(data.Tag, 'VVVV')
end

function test_flash:test_switch_to_bmc()
    local flash_instance = flash.new()
    local ok = pcall(flash_instance.switch_to_bmc, flash_instance)
    lu.assertEquals(ok, false)

    local _, result = pcall(flash_instance.retry_switch_to_bmc, flash_instance)
    lu.assertEquals(result, false)
end

function test_flash:test_switch_to_os()
    local flash_instance = flash.new()
    local ok = pcall(flash_instance.switch_to_os, flash_instance)
    lu.assertEquals(ok, false)

    ok = pcall(flash_instance.retry_switch_to_os, flash_instance)
    lu.assertEquals(ok, true)
end

function test_flash:test_read_from_flash()
    mtd_api.read = function(device_fd, address, length)
        return 'ab', length
    end
    local flash_instance = flash.new()
    flash_instance.device_fd = {}
    local flash_data = flash_instance:read_from_flash({
        Address = 1,
        Length = 2
    })
    lu.assertEquals(flash_data, 'ab')

    flash_data = flash_instance:read({
        Address = 1,
        Length = 2
    })
    lu.assertEquals(flash_data, 'ab')

    flash_data = flash_instance:read_by_type(defs.RegionType.HEADER_DESC)
    lu.assertEquals(flash_data, 'ab')

    flash_data = flash_instance:read_by_type('h')
    lu.assertEquals(flash_data, nil)

    flash_data = flash_instance:read_by_offset(1, 2)
    lu.assertEquals(flash_data, 'ab')
end

function test_flash:test_get_section_info()
    local flash_instance = flash.new()
    local ok = pcall(flash_instance.get_section_info, flash_instance)
    lu.assertEquals(ok, false)

    flash_instance.firmware_desc_info = {}
    flash_instance.firmware_desc_info.FirmwareInfo = '\x01\x01\x01\x00'
    local ok, res = pcall(flash_instance.get_section_info, flash_instance)
    lu.assertEquals(ok, true)
    lu.assertEquals(res.InfoType, 1)
    lu.assertEquals(res.FirmwareNum, 1)
    lu.assertEquals(res.SectionOffset, 1)
end

function test_flash:test_read_firmware_info()
    local flash_instance = flash.new()

    local ok = pcall(flash_instance.read_firmware_info, flash_instance)
    lu.assertEquals(ok, false)

    flash_instance.firmware_section_info ={
        SectionOffset = 1,
        FirmwareNum = 1
    }
    local read = flash_instance.read
    flash_instance.read = function()
        return true
    end
    local ok = pcall(flash_instance.read_firmware_info, flash_instance)
    lu.assertEquals(ok, true)
    flash_instance.read = read
end

function test_flash:test_verify()
    local flash_instance = flash.new()
    local ok = pcall(flash_instance.verify, flash_instance)
    lu.assertEquals(ok, true)
end

function test_flash:test_get_cms_region()
    local flash_instance = flash.new()
    local ok = pcall(flash_instance.get_cms_region, flash_instance)
    lu.assertEquals(ok, false)
end

function test_flash:test_insure_action()
    local ok, _ = pcall(function ()
        bios_app:init_insure_action()
    end)
    lu.assertEquals(ok, true)
    local get_power_status = fructl_handler.get_power_status
    fructl_handler.get_power_status = function ()
        return 'OFF'
    end
    local get_service = bios_factory.get_service
    bios_factory.get_service = function ()
        local obj = {}
        function obj:try_unlock_forever()
            return true
        end
        return obj
    end
    local get_instance = alarm_manager.get_instance
    alarm_manager.get_instance = function ()
        local obj = {}
        function obj:recover()
            return true
        end
        return obj
    end
    ok, _ = pcall(function ()
        bios_app:init_insure_action()
    end)
    fructl_handler.get_power_status = get_power_status
    bios_factory.get_service = get_service
    alarm_manager.get_instance = get_instance
    lu.assertEquals(ok, true)
end

function test_flash:test_set_secure_boot_info()
    local package = gold_package.new(1)
    client.GetSecureBootObjects = function ()
        error('')
    end
    local ok = pcall(function ()
        package:set_secure_boot_info()
    end)
    lu.assertEquals(ok, true)

    package.secure_boot_info = ''
    client.GetSecureBootObjects = function ()
        local objects = {}
        objects[SECURE_BOOT_PATH] = {
            SetBiosSignature = function ()
                error('')
            end
        }
        return objects
    end
    ok = pcall(function ()
        package:set_secure_boot_info()
    end)
    lu.assertEquals(ok, true)
end

function test_flash:test_get_secure_boot_info()
    local package = gold_package.new(1)
    local flash_instance = flash.new()
    flash_instance.get_secure_boot_info = function ()
        return 'abc123'
    end
    local ok = pcall(function ()
        package:get_secure_boot_info(flash_instance)
    end)
    lu.assertEquals(ok, true)
    lu.assertEquals(package.secure_boot_info, 'abc123')
end

function test_flash:test_mtd_api()
    local fd = mtd_api.open('/dev/mtd0', 4098)
    lu.assertEquals(fd < 0, true)
    local _, length = mtd_api.read(fd, 0x0, 96)
    lu.assertEquals(length < 0, true)
end

function test_flash:test_start_verify()
    local bios_ser = bios_service.get_instance()

    local bios_obj = {
        FlashChannelIds = {},
        get_system_id = function()
            return 3
        end,
        register_mdb_objects = function()
            return
        end
    }

    bios_ser:add_object(bios_obj)
    lu.assertEquals(bios_ser.before_power_on_solt[3], true)
    bios_ser.bios_object_collection[3] = bios_obj
    bios_ser.multihost = true
    local ok = pcall(function()
        bios_ser:verify_flash(3)
    end)
    lu.assertEquals(ok, true)

    bios_ser.before_power_on_solt[3] = nil
    bios_ser.multihost = false

    local pfr_ser = pfr_service.get_instance()
    pfr_ser:start_verify(3)
    local pfr_verify_mock = pfr_ser.pfr_verify
    local state_machine_mock = pfr_ser.state_machine
    pfr_ser.state_machine = {
        lock = function ()
        end,
        unlock = function ()
        end
    }
    pfr_ser.pfr_verify = function ()
    end
    pfr_ser.pfr_verify = pfr_verify_mock
    pfr_ser.state_machine = state_machine_mock
end

function test_flash:test_pfr_service()
    local pfr_ser = pfr_service.get_instance()
    local lock_obj = {}
    local lock = {
        lock_until_success = function ()
            lock_obj.is_lock = true
        end,
        lock_forever = function ()
            lock_obj.is_lock_forever = true
        end,
        unlock = function ()
            lock_obj.is_lock = false
        end
    }
    pfr_ser.power_lock_collection[1] = lock
    pfr_ser.gold_package_collection[1] = {
        verify_and_recover = function ()
            return false
        end
    }

    local get_service_mock = bios_factory.get_service
    bios_factory.get_service = function ()
        local obj = {}
        obj.get_package_snapshot = function ()
            local snapshot = {}
            snapshot.get_period = function ()
                return 1
            end
            return snapshot
        end
        obj.get_prop = function ()
            return {1}
        end
        obj.set_prop = function ()
        end
        return obj
    end

    pfr_ser:pfr_verify(1)
    lu.assertEquals(lock_obj.is_lock_forever, true)
    lu.assertEquals(lock_obj.is_lock, true)
    lu.assertEquals(pfr_ser.forever_lock_collection[1], lock)
    bios_factory.get_service = function ()
        local obj = {}
        obj.get_package_snapshot = function ()
            local snapshot = {}
            snapshot.get_period = function ()
                return 2
            end
            return snapshot
        end
        return obj
    end
    local ok, _ = pcall(function ()
        pfr_ser:pfr_verify(1)
    end) 
    lu.assertEquals(ok, true)

    pfr_ser:try_unlock_forever(1)
    lu.assertEquals(lock_obj.is_lock, false)
    lu.assertEquals(pfr_ser.forever_lock_collection[1], nil)

    pfr_ser.running[1] = true
    ok, _ = pcall(function ()
        pfr_ser:start_verify(1)
    end)
    lu.assertEquals(ok, true)
    pfr_ser.running[1] = false

    bios_factory.get_service = function ()
        local obj = {}
        obj.get_package_snapshot = function ()
            return nil
        end
        return obj
    end
    ok, _ = pcall(function ()
        pfr_ser:pfr_verify(1)
    end)
    lu.assertEquals(ok, true)

    ok, _ = pcall(function ()
        pfr_ser:start(0)
    end)
    lu.assertEquals(ok, true)

    local wait_mock = pfr_ser.wait_until_power_off
    pfr_ser.wait_until_power_off = function ()
        return true
    end
    ok, _ = pcall(function ()
        pfr_ser:start(0)
    end)
    lu.assertEquals(ok, true)
    pfr_ser.wait_until_power_off = wait_mock

    pfr_ser.gold_package_collection[1] = nil
    bios_factory.get_service = get_service_mock
end