初次创建仓库提交代码

1. 已经构建好了架子了。
2. 添加了示例的插件
This commit is contained in:
2025-04-21 06:32:47 +00:00
parent 567bcbc0d0
commit 0a0f6a6054
107 changed files with 48783 additions and 2 deletions

View File

@ -0,0 +1,141 @@
ConfigList:
- ipc_iford.nor.glibc-11.1.0-squashfs.ssc029a.512.bga12_ddr4_defconfig
- ipc-rtos_iford.spinand.glibc-11.1.0-ramdisk.ssc029a.512.bga12_ddr4_defconfig
MiReleaseList: []
MhalReleaseList: []
RtosReleaseList:
- coremark
- common
- font
- iqserver
- disp_app
- dualos_pipeline
- aov_preload
- cm4_preload
- preload_rtos
- preload_sample
- lvgl
- usb_gadget_app
- aesdma
- bdma
- camclk
- cpufreq
- drvutil
- emac
- fcie
- flash
- fsp_qspi
- gpio
- i2c
- input
- int
- ir
- kernel
- loadns
- miu
- mmupte
- mspi
- msys
- padmux
- pwm
- rtc
- rtcpwc
- saradc
- sdmmc
- str
- timer
- tsensor
- uart
- voltage
- watchdog
- algo
- decompress
- freertos_plus_fat
- lwfs
- proxyfs
- tcpip
- arm
- riscv
- libc
- newlib_stub
- cam_dev_wrapper
- cam_drv_poll
- cam_fs_wrapper
- cam_os_wrapper
- cam_proc_wrapper
- env_util
- initcall
- loadable_module
- memmang
- memmap
- mempool
- MsWrapper
- newlib_stub
- freertos_main
- freertos_posix
- sys_I_SW
PmRtosReleaseList:
- pm_aesdma
- pm_bdma
- pm_camclk
- pm_cpufreq
- pm_drvutil
- pm_loadns
- pm_emac
- pm_flash
- pm_fsp_qspi
- pm_gpio
- pm_i2c
- pm_int
- pm_ir
- pm_kernel
- pm_miu
- pm_mmupte
- pm_mspi
- pm_msys
- pm_padmux
- pm_pcie
- pm_pl011
- pm_mhal_pm_clk
- pm_pm_idle
- pm_mhal_pm_mbx
- pm_rtcpwc
- pm_mhal_pm_sys
- pm_mhal_pm_wdt
- pm_pspi
- pm_pwm
- pm_pm_power
- pm_rtc
- pm_rtcpwc
- pm_saradc
- pm_sdmmc
- pm_timer
- pm_tsensor
- pm_uart
- pm_voltage
- pm_watchdog
- pm_decompress
- pm_freertos_plus_fat
- pm_lwfs
- pm_proxyfs
- pm_tcpip
- pm_arm
- pm_riscv
- pm_cam_dev_wrapper
- pm_cam_drv_poll
- pm_cam_fs_wrapper
- pm_cam_os_wrapper
- pm_cam_proc_wrapper
- pm_env_util
- pm_initcall
- pm_libc
- pm_memmang
- pm_memmap
- pm_mempool
- pm_MsWrapper
- pm_freertos
- pm_freertos_posix
- pm_sys_I_SW

133
tests/data/test.mk Normal file
View File

@ -0,0 +1,133 @@
# 这是一个测试用的复杂 Makefile 示例
# 包含了各种 Makefile 语法特性用于测试解析器
# 简单变量赋值
SIMPLE_VAR = simple value
# 不同分隔符的变量赋值
COLON_VAR := immediately expanded value
# 多行变量定义
MULTILINE_VAR = first line \
second line \
third line
# 带注释的变量定义
WITH_COMMENT = value with trailing comment # 这是一个注释
# 定义函数
define MULTI_LINE_FUNCTION
echo "This is line 1"
echo "This is line 2"
echo "This is line 3"
endef
# 使用 += 追加变量内容
APPEND_VAR = initial value
APPEND_VAR += appended value
# 条件语句
ifdef DEBUG
LOG_LEVEL = debug
else
LOG_LEVEL = info
endif
# 使用函数
SOURCES = main.c util.c driver.c
OBJECTS = $(patsubst %.c,%.o,$(SOURCES))
# 空变量定义
EMPTY_VAR =
# 带特殊字符的变量定义
SPECIAL_CHARS = $$HOME/bin:/usr/local/bin:$$PATH
# 引用其他变量
COMPOUND_VAR = $(SIMPLE_VAR) and $(COLON_VAR)
# 内容中包含等号的变量
EQUAL_IN_VALUE = key1=value1 key2=value2
# 目标定义
all: build test
# 带多条命令的目标
build:
@echo "Building..." # 注释
$(CC) -o output $(SOURCES)
@echo "Build complete"
# 依赖于多个目标的规则
test: unit_test integration_test
@echo "All tests completed"
# 模式规则
%.o: %.c
$(CC) -c -o $@ $<
# 伪目标
.PHONY: all clean test
# 复杂的多行命令
clean:
@echo "Cleaning up..."
rm -f *.o
rm -f output
@echo "Clean complete"
# 变量中包含换行
NEWLINE := line1$(shell echo)line2
# 带续行符的shell命令
test_shell:
for file in $(SOURCES); do \
echo "Processing $$file"; \
cat $$file | grep "TODO"; \
done
# 使用 ?= 条件赋值
CC ?= gcc
# 使用 != 执行命令并赋值
CURRENT_DIR != pwd
# 包含其他 Makefile
-include optional.mk
include required.mk
# 导出变量到环境变量
export PATH
export JAVA_HOME = /usr/lib/jvm/default-java
# 嵌套使用变量
NESTED = $($(VARNAME))
# 使用自动变量
auto_vars: file1.c file2.c
@echo "First prerequisite: $<"
@echo "All prerequisites: $^"
@echo "Target name: $@"
# 使用通配符
HEADERS = $(wildcard *.h)
# 使用 vpath 指令
vpath %.c src
vpath %.h include
# 静态模式规则
MODULES = mod1 mod2 mod3
$(MODULES:%=%.o): %.o: %.c
$(CC) -c -o $@ $<
# 字符串替换
TEXT = Hello World
REPLACED = $(subst Hello,Goodbye,$(TEXT))
# shell 命令输出作为变量内容
VERSION := $(shell git describe --tags)
# 路径处理
SRC_PATH = src/module/file.c
FILE_NAME = $(notdir $(SRC_PATH))

52
tests/data/test_defconfig Normal file
View File

@ -0,0 +1,52 @@
# 这是一个测试用的复杂 defconfig 文件示例
# 包含了各种 defconfig 语法特性用于测试解析器
# 简单的键值对,使用等号
CONFIG_SIMPLE = y
# 使用等号前后有空格的键值对
CONFIG_WITH_SPACES = y
# 没有空格的键值对
CONFIG_NO_SPACES=y
# 带有数值的配置
CONFIG_INT_VALUE = 100
CONFIG_HEX_VALUE = 0x1000
CONFIG_OCTAL_VALUE = 0755
# 带有字符串的配置
CONFIG_STRING = "hello world"
CONFIG_STRING_NO_QUOTES = hello world
# 带有特殊字符的配置
CONFIG_PATH = /usr/local/bin:/usr/bin:/bin
CONFIG_SPECIAL_CHARS = $HOME/.config
# 带有注释的配置
CONFIG_WITH_COMMENT = y # 这是一个注释
# 多种格式混合
CONFIG_MIXED = "value with spaces" # 和注释
# 空值配置
CONFIG_EMPTY =
# 带有布尔值的配置
CONFIG_BOOL_TRUE = y
CONFIG_BOOL_FALSE = n
# 带有多行内容的配置(实际 defconfig 中不常见,但为测试解析器的健壮性添加)
CONFIG_MULTILINE = first line \
second line \
third line
# 含有等号的值
CONFIG_WITH_EQUAL = key=value
# 重复的键(后面的应该覆盖前面的)
CONFIG_DUPLICATE = first
CONFIG_DUPLICATE = second
# 长路径配置
CONFIG_LONG_PATH = /very/long/path/to/some/file/that/might/wrap/around/in/editor/and/cause/parsing/issues/file.conf

41
tests/test_common.py Normal file
View File

@ -0,0 +1,41 @@
import sys
import os
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
import core.plugin as plugin
def test_plugins():
plugins = plugin.PluginManager()
print("we found {} plugins".format(len(plugins.get_available_plugins())))
print(plugins)
print("Step of BaseLine is:")
print(plugins.BaseLine.PhaseEnv.Pre)
print(plugins.BaseLine.PhaseEnv.Process)
print(plugins.BaseLine.PhaseEnv.Post)
def test_ReleaseStep():
from core.defines import ReleasePhase, ProcessStep
for release_step in ReleasePhase:
print(release_step.value)
for process_step in ProcessStep:
class_name = f" _{release_step.value}_{process_step.value}"
print(class_name)
from database import DirAlias
def test_config():
print("DirAlias.mi_sys: ", DirAlias.mi_sys)
assert DirAlias.mi_sys
def test_demo_list_default():
from database import DemoListDefault
DemoListDefault.check()
print(f"DemoListDefault: {len(DemoListDefault)}")
assert not DemoListDefault
def test_config_single_item():
from database import REPO_URL
assert REPO_URL == "http://hcgit04:9080/manifest/alkaid"

232
tests/test_config_expend.py Normal file
View File

@ -0,0 +1,232 @@
from database import *
def test_black_list_expand():
# 如下的参考数据来源于 原本的脚本里面的 BLACK_LIST_DEFAULT
ref = """
sdk/interface/include/aio/
sdk/interface/include/ai/
sdk/interface/src/ai/
sdk/impl/ai/
sdk/interface/src/aio
sdk/impl/aio
sdk/interface/include/ao/
sdk/interface/src/ao/
sdk/impl/ao/
sdk/interface/include/alsa/
sdk/interface/src/alsa/
sdk/impl/alsa/
sdk/interface/include/cipher/
sdk/interface/src/cipher/
sdk/impl/cipher/
sdk/interface/src/common
sdk/impl/common
sdk/interface/include/cus3a/
sdk/interface/src/cus3a/
sdk/interface/include/debug
sdk/interface/src/debug
sdk/impl/debug
sdk/interface/include/dummy/
sdk/interface/src/dummy/
sdk/impl/dummy/
sdk/interface/include/disp
sdk/interface/src/disp
sdk/impl/disp
sdk/interface/include/dpu/
sdk/interface/src/dpu/
sdk/impl/dpu/
sdk/interface/include/dsp/
sdk/interface/src/dsp/
sdk/impl/dsp/
sdk/interface/include/fb/
sdk/interface/src/fb/
sdk/impl/fb/
sdk/interface/include/gfx/
sdk/interface/src/gfx/
sdk/impl/gfx/
sdk/interface/include/hdmi/
sdk/interface/src/hdmi/
sdk/impl/hdmi/
sdk/interface/include/hdmirx
sdk/interface/src/hdmirx
sdk/impl/hdmirx
sdk/interface/include/hvp
sdk/interface/src/hvp
sdk/impl/hvp
sdk/interface/include/ipu
sdk/interface/src/ipu
sdk/impl/ipu
sdk/interface/include/iqserver/
sdk/interface/src/iqserver/
sdk/interface/include/ive
sdk/interface/src/ive
sdk/impl/ive
sdk/interface/include/jpd/
sdk/interface/src/jpd/
sdk/impl/jpd
sdk/interface/include/mipitx/
sdk/interface/src/mipitx/
sdk/impl/mipitx/
sdk/interface/include/nir
sdk/interface/src/nir
sdk/impl/nir
sdk/interface/include/panel/
sdk/interface/src/panel/
sdk/impl/panel/
sdk/interface/include/pcie/
sdk/interface/src/pcie/
sdk/impl/pcie/
sdk/interface/include/pspi
sdk/interface/src/pspi
sdk/impl/pspi
sdk/interface/include/rgn
sdk/interface/src/rgn
sdk/impl/rgn
sdk/interface/include/scl
sdk/interface/src/scl
sdk/impl/scl
sdk/interface/include/sed/
sdk/interface/src/sed/
sdk/interface/include/sensor
sdk/interface/src/sensor
sdk/impl/sensor
sdk/interface/include/shadow/
sdk/interface/src/shadow/
sdk/impl/shadow/
sdk/impl/sys
sdk/interface/include/vdec/
sdk/interface/src/vdec/
sdk/impl/vdec/
sdk/interface/include/md/
sdk/interface/include/od/
sdk/interface/include/vg/
sdk/interface/include/vdf/
sdk/interface/src/vdf/
sdk/interface/src/vcodec
sdk/interface/include/vdisp/
sdk/interface/src/vdisp/
sdk/impl/vdisp/
sdk/interface/include/wlan/
sdk/interface/src/wlan/
rtos/proj/sc/driver/sysdriver/dualos
rtos/proj/sc/driver/sysdriver/otp
rtos/proj/sc/driver/sysdriver/usb_gadget/udc/usb30
rtos/proj/sc/system/rtos/freertos
rtos/proj/sc_priv/arch/arm/cortex-a/v7_aarch32/context_switch
rtos/proj/sc_priv/arch/arm/cortex-a/v7_aarch32/lh_monitor
rtos/proj/sc_priv/driver/sysdriver/dualos
rtos/proj/sc_priv/driver/sysdriver/otp
rtos/proj/sc_priv/driver/sysdriver/usb_gadget/udc/usb30
rtos/proj/sc_priv/system/rtos/freertos
pm_rtos/proj/sc/driver/sysdriver/usb_gadget/udc/usb30
pm_rtos/proj/sc/driver/camdriver/common
pm_rtos/proj/sc/driver/camdriver/pm_aio
pm_rtos/proj/sc/driver/camdriver/pm_idle
pm_rtos/proj/sc/driver/camdriver/pm_jpe
pm_rtos/proj/sc/driver/camdriver/pm_md
pm_rtos/proj/sc/driver/camdriver/pm_radar
pm_rtos/proj/sc/driver/camdriver/pm_usbpoc
pm_rtos/proj/sc/driver/camdriver/pm_vif
"""
BlackListDefault.check()
ret = sorted(set(config_expand(BlackListDefault)))
ret = [_.strip('/') for _ in ret]
# with open("temp/test_expand.txt", "w") as f:
# for item in ret:
# item = item.strip('/')
# f.write(item + "\n")
exp = [_.strip() for _ in ref.split()]
exp = sorted(set(exp))
exp = [_.strip('/') for _ in exp]
# with open("temp/test_expand_ref.txt", "w") as f:
# for item in exp:
# f.write(item + "\n")
assert ret == exp
#print("BLACK_LIST_DEFAULT.expand: ", ret)
def test_black_list_internal_expand():
BlackListInternal.check()
ref = """
sdk/interface/include/isp/
sdk/interface/src/isp/
sdk/impl/isp/
sdk/interface/include/ispalgo/
sdk/interface/src/ispalgo/
sdk/interface/include/ldc/
sdk/interface/src/ldc/
sdk/impl/ldc/
sdk/interface/include/venc
sdk/interface/src/venc
sdk/impl/venc
sdk/interface/include/vif
sdk/interface/src/vif
sdk/impl/vif
pm_rtos/proj/sc/driver/camdriver/pm_isp/
pm_rtos/proj/sc/driver/camdriver/pm_ispalgo/
pm_rtos/proj/sc/driver/camdriver/pm_radar_algo/
"""
ret = sorted(set(config_expand(BlackListInternal)))
ret = [_.strip('/') for _ in ret]
# with open("temp/test_expand_internal.txt", "w") as f:
# for item in ret:
# f.write(item + "\n")
exp = [_.strip() for _ in ref.split()]
exp = sorted(set(exp))
exp = [_.strip('/') for _ in exp]
# with open("temp/test_expand_ref_internal.txt", "w") as f:
# for item in exp:
# f.write(item + "\n")
assert ret == exp
def test_config_format():
ReleaseOptions.SWBoardAlias = "iford"
@AutoInstanceDecorator
class TestAlias(BaseConfig):
_config = [
"preload_sample",
]
ret = config_expand(TestAlias)
assert ret == ["sdk/verify/sample_code/source/iford/preload_sample/rtos"]
def test_config_test_item():
@AutoInstanceDecorator
class TestAlias(BaseConfig):
_config = [
"preload_sample",
]
TestAlias.update(["xxx/xxx/yyy", "zzz/zzz/www", "test_item"])
ret = config_expand(TestAlias)
# 使用list方式访问
assert [_ for _ in ["xxx/xxx/yyy", "zzz/zzz/www", "test_item"] if _ in ret]
# 使用dict方式访问
assert TestAlias.get("xxx/xxx/yyy") == "xxx/xxx/yyy"
assert TestAlias["zzz/zzz/www"] == "zzz/zzz/www"
# 使用索引方式修改item 因为这是一个list类型的配置所以修改了值其key也会跟着修改
TestAlias["zzz/zzz/www"] = "zzz/zzz/www_new"
assert TestAlias["zzz/zzz/www_new"] == "zzz/zzz/www_new"
# 获取不存在的item
assert TestAlias.get("zzz/zzz/www", 'default') == 'default'
# 特殊访问方式。直接使用一个路径去访问,这样即使这个路径不存在,也不会抛出异常而是返回路径本身
assert TestAlias["zzz/zzz/www"] == "zzz/zzz/www"
# 通过索引的方式访问不存在的item 会抛出异常
try:
TestAlias["unexist_item"]
except KeyError:
assert True
else:
assert False
# 使用点号方式访问
assert TestAlias.test_item == "test_item"
# 使用点号方式设置
TestAlias.test_item = "test_item_new" # 会自动将test_item从_list中删除并添加test_item_new
assert TestAlias.test_item_new == "test_item_new"
# 使用点号方式删除后,再次访问会抛出异常
try:
del TestAlias.test_item_new
TestAlias.test_item_new
except KeyError:
assert True
else:
assert False
# 删除后使用get方式访问会返回默认值
assert TestAlias.get("test_item", "default") == "default"

View File

@ -0,0 +1,209 @@
import sys
import os
import tempfile
import shutil
from pathlib import Path
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
from utils.utils import DefconfigParser
def test_defconfig_parser_read():
"""测试 DefconfigParser 类的读取功能"""
defconfig_path = os.path.join(os.path.dirname(__file__), 'data', 'test_defconfig')
parser = DefconfigParser(defconfig_path)
temp_file1 = os.path.join(os.path.dirname(__file__), '.tmp', 'test_defconfig_parsed.json')
with open(temp_file1, 'w') as f:
f.write(f"{parser}")
# 测试获取简单键值对
assert parser.get('CONFIG_SIMPLE') == 'y'
assert parser['CONFIG_SIMPLE'] == 'y' # 使用索引方式访问
assert parser.get('CONFIG_WITH_SPACES') == 'y'
assert parser.get('CONFIG_NO_SPACES') == 'y'
# 测试获取数值配置
assert parser.get('CONFIG_INT_VALUE') == '100'
assert parser.get('CONFIG_HEX_VALUE') == '0x1000'
assert parser.get('CONFIG_OCTAL_VALUE') == '0755'
# 测试获取字符串配置
assert parser.get('CONFIG_STRING') == '"hello world"'
assert parser.get('CONFIG_STRING_NO_QUOTES') == 'hello world'
# 测试获取特殊字符配置
assert parser.get('CONFIG_PATH') == '/usr/local/bin:/usr/bin:/bin'
assert parser.get('CONFIG_SPECIAL_CHARS') == '$HOME/.config'
# 测试带有注释的配置
assert parser.get('CONFIG_WITH_COMMENT') == 'y'
# 测试混合格式
assert parser.get('CONFIG_MIXED') == '"value with spaces"'
# 测试空值配置
assert parser.get('CONFIG_EMPTY') == ''
# 测试布尔值配置
assert parser.get('CONFIG_BOOL_TRUE') == 'y'
assert parser.get('CONFIG_BOOL_FALSE') == 'n'
# 测试多行内容配置 (在实际解析中可能会有所不同,取决于实现)
multiline_value = parser.get('CONFIG_MULTILINE')
assert 'first line' in multiline_value
# 测试含有等号的值
assert parser.get('CONFIG_WITH_EQUAL') == 'key=value'
# 测试重复的键 (应该获取最后一个值)
assert parser.get('CONFIG_DUPLICATE') == 'second'
# 测试长路径配置
assert '/very/long/path' in parser.get('CONFIG_LONG_PATH')
# 测试不存在的键
assert parser.get('CONFIG_NOT_EXIST', 'default') == 'default'
def test_defconfig_parser_rw():
"""测试 DefconfigParser 类的读写功能,看是否能够正确写回文件"""
src_file = os.path.join(os.path.dirname(__file__), 'data', 'test_defconfig')
parser = DefconfigParser(src_file)
temp_dir = tempfile.mkdtemp()
if 1:
new_file = os.path.join(os.path.dirname(__file__), '.tmp', 'test_defconfig_copy')
else:
new_file = os.path.join(temp_dir, 'test_defconfig_copy')
# temp_file1 = os.path.join(os.path.dirname(__file__), 'test_defconfig_parsed.json')
# with open(temp_file1, 'w') as f:
# f.write(f"{parser}")
parser.flush(new_file)
try:
with open(new_file, 'r') as f:
with open(src_file, 'r') as f2:
# 由于一些格式控制符,导致读取的文件与原文件不一致,这里忽略空格进行比较
src = f2.read().replace(' ', '')
dst = f.read().replace(' ', '')
assert src == dst
finally:
# 清理临时文件
if os.path.exists(temp_dir):
shutil.rmtree(temp_dir)
def test_defconfig_parser_modify():
"""测试 DefconfigParser 类的修改功能"""
# 创建测试文件的副本
temp_dir = tempfile.mkdtemp()
temp_file = os.path.join(temp_dir, 'test_defconfig_copy')
original_file = os.path.join(os.path.dirname(__file__), 'data', 'test_defconfig')
shutil.copy(original_file, temp_file)
try:
parser = DefconfigParser(temp_file)
# 修改现有配置
parser['CONFIG_SIMPLE'] = 'n'
parser['CONFIG_INT_VALUE'] = '200'
parser['CONFIG_STRING'] = '"modified string"'
# 添加新配置
parser['CONFIG_NEW'] = 'new_value'
# 删除配置
del parser['CONFIG_EMPTY']
idx = parser.index('CONFIG_SIMPLE')
parser.insert(idx, 'CONFIG_SIMPLE_NEW', '=', 'new_value_new')
# 刷新到文件
parser.flush()
# temp_file1 = os.path.join(os.path.dirname(__file__), 'test_defconfig_parsed.json')
# with open(temp_file1, 'w') as f:
# f.write(f"{parser}")
# 重新加载验证修改
new_parser = DefconfigParser(temp_file)
assert new_parser.get('CONFIG_SIMPLE') == 'n'
assert new_parser.get('CONFIG_INT_VALUE') == '200'
assert new_parser.get('CONFIG_STRING') == '"modified string"'
assert new_parser.get('CONFIG_NEW') == 'new_value'
assert new_parser.get('CONFIG_EMPTY') is None # 删除的配置
assert new_parser.get('CONFIG_SIMPLE_NEW') == 'new_value_new' # 插入的配置
finally:
# 清理临时文件
if os.path.exists(temp_dir):
shutil.rmtree(temp_dir)
def test_defconfig_parser_content_preservation():
"""测试 DefconfigParser 类是否保留原始文件的注释和结构"""
# 创建测试文件的副本
temp_dir = tempfile.mkdtemp()
temp_file = os.path.join(temp_dir, 'test_defconfig_copy')
original_file = os.path.join(os.path.dirname(__file__), 'data', 'test_defconfig')
shutil.copy(original_file, temp_file)
try:
# 记录原始文件的行数和注释行数
with open(temp_file, 'r') as f:
original_lines = f.readlines()
original_line_count = len(original_lines)
original_comment_count = sum(1 for line in original_lines if line.strip().startswith('#'))
# 进行一些简单修改
parser = DefconfigParser(temp_file)
parser['CONFIG_SIMPLE'] = 'n'
parser.flush()
# 检查修改后的文件结构是否保留
with open(temp_file, 'r') as f:
modified_lines = f.readlines()
modified_line_count = len(modified_lines)
modified_comment_count = sum(1 for line in modified_lines if line.strip().startswith('#'))
# 行数应该大致相同
assert abs(original_line_count - modified_line_count) < 5
# 注释应该完全保留
assert original_comment_count == modified_comment_count
finally:
# 清理临时文件
if os.path.exists(temp_dir):
shutil.rmtree(temp_dir)
def test_edge_cases():
"""测试 DefconfigParser 类的边缘情况处理"""
# 创建一个临时的 defconfig 文件,包含一些边缘情况
temp_dir = tempfile.mkdtemp()
temp_file = os.path.join(temp_dir, 'edge_case_defconfig')
try:
# 创建一个包含边缘情况的 defconfig 文件
with open(temp_file, 'w') as f:
f.write("# 空文件开始\n\n")
f.write("# 只有注释的行\n")
f.write("CONFIG_NO_VALUE=\n") # 无值
f.write("CONFIG_SPACES_AROUND_EQUAL = value_with_spaces_around_equal\n") # 等号周围有多个空格
f.write("=invalid_line_no_key\n") # 无效行,没有键
f.write("invalid_line_no_equal\n") # 无效行,没有等号
# 解析文件并测试
parser = DefconfigParser(temp_file)
# 检查无值配置
assert parser.get('CONFIG_NO_VALUE') == ''
# 检查等号周围有多个空格的配置
assert parser.get('CONFIG_SPACES_AROUND_EQUAL') == 'value_with_spaces_around_equal'
# 检查无效行处理
assert parser.get('invalid_line_no_equal', 'not_found') == 'not_found'
# 修改并保存
parser['CONFIG_NO_VALUE'] = 'now_has_value'
parser.flush()
# 重新加载并验证
new_parser = DefconfigParser(temp_file)
assert new_parser.get('CONFIG_NO_VALUE') == 'now_has_value'
finally:
# 清理临时文件
if os.path.exists(temp_dir):
shutil.rmtree(temp_dir)

442
tests/test_flow.py Normal file
View File

@ -0,0 +1,442 @@
from pathlib import Path
import pytest
import sys, os
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
from thirdparty.py_trees.trees import BehaviourTree
from core.behavior_tree import ReleaseFlowActionDecorator, ReleaseFlowBuilder, ReleaseFlowAction
from thirdparty.py_trees.common import Status, Access
from core.logger import get_logger
log = get_logger()
class ActionTest(ReleaseFlowAction):
"""测试用的Action节点"""
def __init__(self, name: str, return_status: Status, description: str = ""):
super().__init__(name, description)
self.return_status = return_status
self.process_called = False
self.setup_called = False
self.shutdown_called = False
def setup(self):
self.setup_called = True
log.info(f"setup: {self.name}")
def process(self):
self.process_called = True
self.status = self.return_status
log.info(f"process: {self.name}, status: {self.status}")
def update(self):
return self.status
def shutdown(self):
self.shutdown_called = True
class ActionTestBlackboard(ReleaseFlowAction):
"""测试Blackboard功能的Action节点"""
def __init__(self, name: str, description: str = ""):
super().__init__(name, description)
self.blackboard.register_key("test_key", access=Access.WRITE)
self.blackboard.register_key("read_only_key", access=Access.READ)
def process(self):
self.status = Status.SUCCESS
log.info(f"process: {self.name}, status: {self.status}, test_key: {self.blackboard.test_key}")
def update(self):
return self.status
class ActionTestBlackboard1(ReleaseFlowAction):
"""测试Blackboard功能的Action节点"""
def __init__(self, name: str, description: str = ""):
super().__init__(name, description)
self.blackboard.require_key("test_key")
def process(self):
self.status = Status.SUCCESS
log.info(f"process: {self.name}, status: {self.status}, test_key: {self.blackboard.test_key}")
def update(self):
return self.status
@pytest.fixture
def builder():
"""创建ReleaseFlowBuilder实例的fixture"""
return ReleaseFlowBuilder()
def test_single_action():
"""测试单个Action节点的执行"""
action = ActionTest("test_action", Status.SUCCESS)
action.setup()
assert action.setup_called
action.tick_once()
assert action.process_called
assert action.status == Status.SUCCESS
action.shutdown()
assert action.shutdown_called
def test_sequence(builder):
"""测试Sequence节点的执行"""
success_action = ActionTest("success_action", Status.SUCCESS)
failure_action = ActionTest("failure_action", Status.FAILURE)
with builder.sequence("test_sequence") as b:
b.action(success_action)
b.action(failure_action)
flow = builder.build()
tree = BehaviourTree(root=flow)
tree.setup()
tree.tick()
assert failure_action.process_called
assert flow.status == Status.FAILURE
assert success_action.process_called
def test_selector(builder):
"""测试Selector节点的执行"""
failure_action = ActionTest("failure_action", Status.FAILURE)
success_action = ActionTest("success_action", Status.SUCCESS)
with builder.selector("test_selector") as b:
b.action(failure_action)
b.action(success_action)
flow = builder.build()
tree = BehaviourTree(root=flow)
tree.setup()
tree.tick()
assert flow.status == Status.SUCCESS
assert failure_action.process_called
assert success_action.process_called
def test_blackboard():
"""测试Blackboard功能"""
action = ActionTestBlackboard("test_blackboard")
action1 = ActionTestBlackboard1("test_blackboard_read")
# 测试写入权限
action.blackboard.test_key = "test_value"
# 测试共享
assert action1.blackboard.test_key == "test_value"
# 测试只读权限
with pytest.raises(AttributeError):
action.blackboard.read_only_key = "new_value"
# 测试必需key
with pytest.raises(AttributeError):
_ = action.blackboard.required_key
def test_parallel(builder):
"""测试Parallel节点的执行"""
success_action1 = ActionTest("success_action1", Status.SUCCESS)
success_action2 = ActionTest("success_action2", Status.SUCCESS)
failure_action = ActionTest("failure_action", Status.FAILURE)
with builder.parallel("test_parallel") as b:
b.action(success_action1)
b.action(success_action2)
b.action(failure_action)
flow = builder.build()
tree = BehaviourTree(root=flow)
tree.setup()
tree.tick()
assert flow.status == Status.FAILURE
assert success_action1.process_called
assert success_action2.process_called
assert failure_action.process_called
def test_nested_behavior_tree(builder):
"""测试嵌套的行为树结构"""
success_action1 = ActionTest("success_action1", Status.SUCCESS)
success_action2 = ActionTest("success_action2", Status.SUCCESS)
failure_action = ActionTest("failure_action", Status.FAILURE)
with builder.sequence("root_sequence") as b:
with b.selector("nested_selector") as s:
s.action(failure_action)
s.action(success_action1)
b.action(success_action2)
flow = builder.build()
tree = BehaviourTree(root=flow)
tree.setup()
tree.tick()
assert flow.status == Status.SUCCESS
assert failure_action.process_called
assert success_action1.process_called
assert success_action2.process_called
def test_parallel_success_on_all(builder):
"""测试并行节点SuccessOnAll策略"""
success_action1 = ActionTest("success_action1", Status.SUCCESS)
success_action2 = ActionTest("success_action2", Status.SUCCESS)
success_action3 = ActionTest("success_action3", Status.SUCCESS)
with builder.parallel("test_parallel_success_on_all") as b:
b.action(success_action1)
b.action(success_action2)
b.action(success_action3)
flow = builder.build()
tree = BehaviourTree(root=flow)
tree.setup()
tree.tick()
assert flow.status == Status.SUCCESS
assert success_action1.process_called
assert success_action2.process_called
assert success_action3.process_called
def test_parallel_success_on_one(builder):
"""测试并行节点SuccessOnOne策略"""
from thirdparty.py_trees.common import ParallelPolicy
running_action1 = ActionTest("running_action1", Status.RUNNING)
success_action = ActionTest("success_action", Status.SUCCESS)
running_action2 = ActionTest("running_action2", Status.RUNNING)
with builder.parallel("test_parallel_success_on_one",
policy=ParallelPolicy.SuccessOnOne()) as b:
b.action(running_action1)
b.action(success_action)
b.action(running_action2)
flow = builder.build()
tree = BehaviourTree(root=flow)
tree.setup()
tree.tick()
assert flow.status == Status.SUCCESS
assert running_action1.process_called
assert success_action.process_called
assert running_action2.process_called
def test_multiple_executions(builder):
"""测试行为树的多次执行"""
action1 = ActionTest("action1", Status.SUCCESS)
action2 = ActionTest("action2", Status.SUCCESS)
with builder.sequence("test_multiple_executions") as b:
b.action(action1)
b.action(action2)
flow = builder.build()
tree = BehaviourTree(root=flow)
tree.setup()
# 第一次执行
tree.tick()
assert flow.status == Status.SUCCESS
assert action1.process_called
assert action2.process_called
# 重置状态
action1.process_called = False
action2.process_called = False
# 第二次执行
tree.tick()
assert flow.status == Status.SUCCESS
assert action1.process_called
assert action2.process_called
def test_error_handling(builder):
"""测试行为树的错误处理"""
# 测试空的行为树
with pytest.raises(Exception) as excinfo:
builder.build()
assert "No nodes added to behavior tree" in str(excinfo.value)
# 测试未闭合的节点
with pytest.raises(Exception) as excinfo:
with builder.sequence("unclosed_sequence") as b:
b.action(ActionTest("action", Status.SUCCESS))
b.action(ActionTest("action", Status.SUCCESS))
# 不调用end()或使用with语句的__exit__
builder.build()
assert "Unclosed nodes" in str(excinfo.value)
class ActionWithError(ReleaseFlowAction):
"""测试异常处理的Action节点"""
def __init__(self, name: str, description: str = ""):
super().__init__(name, description)
self.process_called = False
def process(self):
self.process_called = True
raise Exception("测试异常")
def update(self):
return Status.FAILURE
def test_action_exception_handling(builder):
"""测试Action节点异常处理"""
error_action = ActionWithError("error_action")
with builder.sequence("test_error_handling") as b:
b.action(error_action)
flow = builder.build()
tree = BehaviourTree(root=flow)
tree.setup()
# 执行时应该捕获异常并返回FAILURE状态
tree.tick()
assert error_action.process_called
assert flow.status == Status.FAILURE
def test_decorator():
"""测试ReleaseFlowActionDecorator装饰器"""
@ReleaseFlowActionDecorator
class DecoratedAction(ReleaseFlowAction):
def process(self):
self.status = Status.SUCCESS
def update(self):
return self.status
# 测试自动生成名称
action1 = DecoratedAction()
assert action1.name == f"{Path(__file__).stem}.DecoratedAction"
# 测试自定义名称
action2 = DecoratedAction(name="custom_name")
assert action2.name == "custom_name"
# 测试功能
action2.setup()
action2.tick_once()
assert action2.status == Status.SUCCESS
def test_complex_behavior_tree(builder):
"""测试复杂的行为树结构"""
success_action1 = ActionTest("success_action1", Status.SUCCESS)
success_action2 = ActionTest("success_action2", Status.SUCCESS)
failure_action1 = ActionTest("failure_action1", Status.FAILURE)
success_action3 = ActionTest("success_action3", Status.SUCCESS)
with builder.sequence("root") as b:
with b.parallel("parallel_group") as p:
p.action(success_action1)
p.action(success_action2)
with b.selector("selector_group") as s:
s.action(failure_action1)
s.action(success_action3)
flow = builder.build()
tree = BehaviourTree(root=flow)
tree.setup()
tree.tick()
assert flow.status == Status.SUCCESS
assert success_action1.process_called
assert success_action2.process_called
assert failure_action1.process_called
assert success_action3.process_called
def test_blackboard_advanced():
"""测试Blackboard的高级功能"""
class ActionWrite(ReleaseFlowAction):
def __init__(self, name: str, description: str = ""):
super().__init__(name, description)
self.blackboard.register_key("counter", access=Access.WRITE)
self.blackboard.counter = 0
def process(self):
self.blackboard.counter += 1
self.status = Status.SUCCESS
def update(self):
return self.status
class ActionRead(ReleaseFlowAction):
def __init__(self, name: str, description: str = ""):
super().__init__(name, description)
self.blackboard.register_key("counter", access=Access.READ)
self.value = 0
def process(self):
self.value = self.blackboard.counter
self.status = Status.SUCCESS
def update(self):
return self.status
# 创建动作节点
write_action = ActionWrite("write_action")
read_action = ActionRead("read_action")
# 设置初始值
write_action.blackboard.counter = 10
# 测试读取
assert read_action.blackboard.counter == 10
# 测试写入
write_action.blackboard.counter = 20
assert read_action.blackboard.counter == 20
# 测试只读权限
with pytest.raises(AttributeError):
read_action.blackboard.counter = 30
def test_action_exception_handling_with_sequence(builder):
"""测试Action节点异常处理后行为树继续执行的能力"""
error_action = ActionWithError("error_action")
success_action = ActionTest("success_action", Status.SUCCESS)
with builder.sequence("test_error_handling_sequence") as b:
b.action(error_action)
b.action(success_action)
flow = builder.build()
tree = BehaviourTree(root=flow)
tree.setup()
# 执行时应该捕获异常并返回FAILURE状态但不会中断行为树的执行
tree.tick()
# 验证error_action被标记为已处理
assert error_action.process_called
# 验证序列节点状态为FAILURE因为第一个节点失败
assert flow.status == Status.FAILURE
# 验证success_action没有被执行因为序列节点在第一个节点失败后停止
assert not success_action.process_called
def test_action_exception_handling_with_selector(builder):
"""测试Action节点异常处理后选择器继续尝试其他节点"""
error_action = ActionWithError("error_action")
success_action = ActionTest("success_action", Status.SUCCESS)
with builder.selector("test_error_handling_selector") as b:
b.action(error_action)
b.action(success_action)
flow = builder.build()
tree = BehaviourTree(root=flow)
tree.setup()
# 执行时应该捕获异常并继续尝试下一个节点
tree.tick()
# 验证error_action被标记为已处理
assert error_action.process_called
# 验证success_action被执行
assert success_action.process_called
# 验证选择器节点状态为SUCCESS因为第二个节点成功
assert flow.status == Status.SUCCESS

View File

@ -0,0 +1,141 @@
import sys
import os
import tempfile
import shutil
from pathlib import Path
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
from utils.utils import MakefileParser
def test_makefile_parser_read():
"""测试 MakefileParser 类的读取功能"""
parser = MakefileParser(os.path.join(os.path.dirname(__file__), "data", "test.mk"))
with open(os.path.join(os.path.dirname(__file__), '.tmp', 'test_parsed.json'), 'w') as f:
f.write(f"{parser}")
# 测试获取简单变量
assert parser.get('SIMPLE_VAR') == ['simple', 'value']
# 测试获取使用不同分隔符的变量
assert parser.get('COLON_VAR') == ['immediately', 'expanded', 'value']
# 测试获取多行变量
multiline = parser.get('MULTILINE_VAR')
assert len(multiline) == 6
# 测试带注释的变量
assert parser.get('WITH_COMMENT')[0].startswith('value')
# 测试不存在的变量返回默认值
assert parser.get('NON_EXISTENT', 'default') == 'default'
# 测试复合变量
assert parser.get('COMPOUND_VAR') == ['$(SIMPLE_VAR)', 'and', '$(COLON_VAR)']
# 测试空变量
assert parser.get('EMPTY_VAR') == []
def test_makefile_parser_rw():
"""测试 MakefileParser 类的读写功能,看是否能够正确写回文件"""
src_file = os.path.join(os.path.dirname(__file__), "data", "test.mk")
parser = MakefileParser(src_file)
# new_file = os.path.join(os.path.dirname(__file__), 'test_rw.mk')
temp_dir = tempfile.mkdtemp()
new_file = os.path.join(temp_dir, 'test_copy.mk')
shutil.copy(os.path.join(os.path.dirname(__file__), "data", "test.mk"), new_file)
parser.flush(new_file)
try:
with open(new_file, 'r') as f:
with open(src_file, 'r') as f2:
# 由于一些格式控制符,导致读取的文件与原文件不一致,这里忽略空格进行比较
src = f2.read().replace(' ', '')
dst = f.read().replace(' ', '')
assert src == dst
finally:
# 清理临时文件
if os.path.exists(temp_dir):
shutil.rmtree(temp_dir)
def test_makefile_parser_modify():
"""测试 MakefileParser 类的修改功能"""
# 创建测试文件的副本
temp_dir = tempfile.mkdtemp()
temp_file = os.path.join(temp_dir, 'test_copy.mk')
shutil.copy(os.path.join(os.path.dirname(__file__), "data", "test.mk"), temp_file)
try:
parser = MakefileParser(temp_file)
# 修改现有变量
parser['SIMPLE_VAR'] = 'modified value'
# 添加新变量
parser['NEW_VAR'] = 'new value'
#在SIMPLE_VAR前面插入新变量
# idx = parser.index('SIMPLE_VAR')
parser.insert('SIMPLE_VAR', 'NEW_VAR1', ':=', 'new value', '# comment')
# 添加多行变量
parser['NEW_MULTILINE'] = ['line 1', 'line 2', 'line 3']
# 删除变量
del parser['EMPTY_VAR']
# 使用正则
parser['REGEX_VAR'] = 'new value'
parser['REGEX_.* := new value'] = 'REGEX_VAR := regex value'
# 刷新到文件
parser.flush()
with open(os.path.join(os.path.dirname(__file__), '.tmp', 'test_parsed.json'), 'w') as f:
f.write(f"{parser}")
# 重新加载解析器验证修改
new_parser = MakefileParser(temp_file)
assert new_parser.get('SIMPLE_VAR') == ['modified', 'value']
assert new_parser.get('NEW_VAR') == ['new', 'value']
assert new_parser.get('NEW_VAR1') == ['new', 'value']
assert len(new_parser.get('NEW_MULTILINE')) == 6
assert new_parser.get('EMPTY_VAR') is None
assert new_parser.get('REGEX_VAR') == ['regex', 'value']
del new_parser['REGEX_VAR']
assert new_parser.get('REGEX_VAR') is None
finally:
# 清理临时文件
if os.path.exists(temp_dir):
shutil.rmtree(temp_dir)
def test_makefile_parser_content_preservation():
"""测试 MakefileParser 类是否保留原始文件的注释和结构"""
# 创建测试文件的副本
temp_dir = tempfile.mkdtemp()
temp_file = os.path.join(temp_dir, 'test_copy.mk')
shutil.copy(os.path.join(os.path.dirname(__file__), "data", "test.mk"), temp_file)
try:
# 记录原始文件内容的行数和注释行数
with open(temp_file, 'r') as f:
original_lines = f.readlines()
original_line_count = len(original_lines)
original_comment_count = sum(1 for line in original_lines if line.strip().startswith('#'))
# 使用解析器进行一些简单修改
parser = MakefileParser(temp_file)
parser['SIMPLE_VAR'] = 'modified but preserving structure'
parser.flush()
# 检查修改后的文件是否保留了原始结构
with open(temp_file, 'r') as f:
modified_lines = f.readlines()
modified_line_count = len(modified_lines)
modified_comment_count = sum(1 for line in modified_lines if line.strip().startswith('#'))
assert original_line_count == modified_line_count
# 注释应该完全保留
assert original_comment_count == modified_comment_count
finally:
# 清理临时文件
if os.path.exists(temp_dir):
shutil.rmtree(temp_dir)

View File

@ -0,0 +1,291 @@
import sys
import os
from unittest.mock import patch, MagicMock
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
from database.options import ReleaseOptions
def test_basic_required_args():
"""测试基本必需参数"""
# 构建必需参数
test_args = [
'--SWBoardAlias', 'testboard',
'--TagId', 'v1.0.0',
'--SnapShotXml', 'snapshot.xml',
'--SWBoardType', 'boardtype',
'--SWProductType', 'producttype'
]
# 解析参数
ReleaseOptions.parse(test_args)
# 验证必需参数被正确解析 使用属性的方式访问
assert ReleaseOptions.SWBoardAlias == 'testboard'
assert ReleaseOptions.TagId == 'v1.0.0'
assert ReleaseOptions.SnapShotXml == 'snapshot.xml'
assert ReleaseOptions.SWBoardType == 'boardtype'
assert ReleaseOptions.SWProductType == 'producttype'
# 验证非必需参数使用默认值
assert ReleaseOptions['ThreadNum'] == 1
assert ReleaseOptions['IplVersion'] == 'CN'
assert ReleaseOptions['SkipCheckout'] is False
assert ReleaseOptions['OpenSource'] is False
assert ReleaseOptions.check() is True
def test_bool_args():
"""测试布尔类型的参数"""
test_args = [
'--SWBoardAlias', 'testboard',
'--TagId', 'v1.0.0',
'--SnapShotXml', 'snapshot.xml',
'--SWBoardType', 'boardtype',
'--SWProductType', 'producttype',
'--SkipCheckout',
'--OpenSource',
'--RiscVSupport',
'--Cm4Support',
'--Doc2Pdf',
'--Doc2Html',
'--VerifyBuild',
'--ReduceCodeSize',
'--SkipSyncCode'
]
ReleaseOptions.parse(test_args)
# 验证布尔参数被正确解析
assert ReleaseOptions['SkipCheckout'] is True
assert ReleaseOptions['OpenSource'] is True
assert ReleaseOptions['RiscVSupport'] is True
assert ReleaseOptions['Cm4Support'] is True
assert ReleaseOptions['Doc2Pdf'] is True
assert ReleaseOptions['Doc2Html'] is True
assert ReleaseOptions['VerifyBuild'] is True
assert ReleaseOptions['ReduceCodeSize'] is True
assert ReleaseOptions['SkipSyncCode'] is True
assert ReleaseOptions.check() is True
def test_optional_args():
"""测试可选参数"""
test_args = [
'--SWBoardAlias', 'testboard',
'--TagId', 'v1.0.0',
'--SnapShotXml', 'snapshot.xml',
'--SWBoardType', 'boardtype',
'--SWProductType', 'producttype',
'--DocsPath', '/path/to/docs',
'--HWChipType', 'chiptype',
'--HWPackageType', 'package',
'--OutPath', 'output_dir',
'--ThreadNum', '8',
'--IplVersion', 'WW'
]
ReleaseOptions.parse(test_args)
# 验证可选参数被正确解析
assert ReleaseOptions['DocsPath'] == '/path/to/docs'
assert ReleaseOptions['HWChipType'] == 'chiptype'
assert ReleaseOptions['HWPackageType'] == 'package'
assert ReleaseOptions['OutPath'] == 'output_dir'
assert ReleaseOptions['ThreadNum'] == 8
assert ReleaseOptions['IplVersion'] == 'WW'
assert ReleaseOptions.check() is True
def test_mix_args():
"""测试混合使用短格式和长格式参数"""
test_args = [
'-a', 'testboard',
'-t', 'v1.0.0',
'-s', 'snapshot.xml',
'-b', 'boardtype',
'-p', 'producttype',
'-d', '/path/to/docs',
'-j', '4',
'-i', # SkipCheckout
'-f' # OpenSource
]
ReleaseOptions.parse(test_args)
# 验证短格式参数被正确解析
assert ReleaseOptions['SWBoardAlias'] == 'testboard'
assert ReleaseOptions['TagId'] == 'v1.0.0'
assert ReleaseOptions['DocsPath'] == '/path/to/docs'
assert ReleaseOptions['ThreadNum'] == 4
assert ReleaseOptions['SkipCheckout'] is True
assert ReleaseOptions['OpenSource'] is True
assert ReleaseOptions.check() is True
def test_missing_required_args():
"""测试缺少必需参数的情况"""
# 缺少必需参数 SWBoardType
test_args = [
'--SWBoardAlias', 'testboard',
'--TagId', 'v1.0.0',
'--SnapShotXml', 'snapshot.xml',
'--SWProductType', 'producttype'
]
try:
ReleaseOptions.parse(test_args)
except SystemExit as e:
assert True
def test_invalid_thread_num():
"""测试无效的线程数"""
test_args = [
'--SWBoardAlias', 'testboard',
'--TagId', 'v1.0.0',
'--SnapShotXml', 'snapshot.xml',
'--SWBoardType', 'boardtype',
'--SWProductType', 'producttype',
'--ThreadNum', '-1' # 无效的线程数
]
ReleaseOptions.parse(test_args)
# 线程数被解析但无效
assert ReleaseOptions['ThreadNum'] == -1
# 检查应该失败
try:
ReleaseOptions.check()
except ValueError as e:
assert str(e) == "ThreadNum 必须为正整数"
def test_ipl_version_choices():
"""测试 IplVersion 的选择限制"""
# 备份原始的 ReleaseOptions 实例
original_instance = ReleaseOptions._instance
try:
# 有效的 IplVersion: 'CN'
test_args = [
'--SWBoardAlias', 'testboard',
'--TagId', 'v1.0.0',
'--SnapShotXml', 'snapshot.xml',
'--SWBoardType', 'boardtype',
'--SWProductType', 'producttype',
'--IplVersion', 'CN'
]
ReleaseOptions.parse(test_args)
assert ReleaseOptions['IplVersion'] == 'CN'
assert ReleaseOptions.check() is True
# 有效的 IplVersion: 'WW'
test_args[-1] = 'WW'
ReleaseOptions.parse(test_args)
assert ReleaseOptions['IplVersion'] == 'WW'
assert ReleaseOptions.check() is True
# 无效的 IplVersion
test_args[-1] = 'INVALID'
try:
ReleaseOptions.parse(test_args)
except SystemExit as e:
assert True
finally:
# 恢复原始的 ReleaseOptions 实例
ReleaseOptions._instance = original_instance
@patch('database.options.get_alkaid_root') # 修改patch路径为正确的模块路径
def test_default_alkaid_root_path(mock_get_alkaid_root):
"""测试默认的 AlkaidRootPath"""
# 设置 mock 返回值
mock_get_alkaid_root.return_value = '/mocked/alkaid/root'
test_args = [
'--SWBoardAlias', 'testboard',
'--TagId', 'v1.0.0',
'--SnapShotXml', 'snapshot.xml',
'--SWBoardType', 'boardtype',
'--SWProductType', 'producttype'
]
# 重置 ReleaseOptions 实例,确保使用新的 mock
ReleaseOptions._instance = None
# 重新初始化 ReleaseOptions 实例
ReleaseOptions.__class__().parse(test_args)
# 验证 AlkaidRootPath 使用 get_alkaid_root 的返回值
assert ReleaseOptions['AlkaidRootPath'] == '/mocked/alkaid/root'
mock_get_alkaid_root.assert_called_once()
def test_dict_interface():
"""测试字典接口功能"""
test_args = [
'--SWBoardAlias', 'testboard',
'--TagId', 'v1.0.0',
'--SnapShotXml', 'snapshot.xml',
'--SWBoardType', 'boardtype',
'--SWProductType', 'producttype'
]
ReleaseOptions.parse(test_args)
# 测试 __getitem__
assert ReleaseOptions['SWBoardAlias'] == 'testboard'
# 测试 __setitem__
ReleaseOptions['OutPath'] = 'custom_output'
assert ReleaseOptions['OutPath'] == 'custom_output'
# 测试 __contains__
assert 'SWBoardAlias' in ReleaseOptions
assert 'OutPath' in ReleaseOptions
assert 'NonExistentKey' not in ReleaseOptions
def test_mounriver_sdk_with_riscv():
"""测试 MounRiverSDK 和 RiscVSupport 的组合"""
test_args = [
'--SWBoardAlias', 'testboard',
'--TagId', 'v1.0.0',
'--SnapShotXml', 'snapshot.xml',
'--SWBoardType', 'boardtype',
'--SWProductType', 'producttype',
'--RiscVSupport',
'--MounRiverSDK'
]
ReleaseOptions.parse(test_args)
# 验证两个参数都被正确设置
assert ReleaseOptions['RiscVSupport'] is True
assert ReleaseOptions['MounRiverSDK'] is True
assert ReleaseOptions.check() is True
def test_help_method():
"""测试 help 方法"""
# 备份原始解析器
original_parser = getattr(ReleaseOptions, 'parser', None)
try:
# 准备一个 MagicMock 来捕获 print_help 的调用
ReleaseOptions.parser = MagicMock()
# 调用 help 方法
ReleaseOptions.help()
# 验证 print_help 被调用
ReleaseOptions.parser.print_help.assert_called_once()
finally:
# 恢复原始解析器
ReleaseOptions.parser = original_parser

25
tests/test_yaml.py Normal file
View File

@ -0,0 +1,25 @@
import sys,os
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
from thirdparty import yaml
def test_yaml_base():
yaml_str = """
name: Alice
age: 25
hobbies:
- reading
- hiking
"""
data = yaml.safe_load(yaml_str) # 推荐使用 safe_load 避免潜在的安全风险
print(data, f"{type(data)}")
def test_load_customer_yaml():
file = os.path.join(os.path.dirname(__file__), "data", "customer_test.yaml")
with open(file, "r") as fp:
yaml_str = fp.read()
data = yaml.safe_load(yaml_str) # 推荐使用 safe_load 避免潜在的安全风险
print(data, f"{type(data)}")