From 01e72ecd5ef4de892638b59ec34e4fd750d9e3d2 Mon Sep 17 00:00:00 2001 From: leesum <1255273338@qq.com> Date: Sat, 15 Feb 2025 17:06:01 +0800 Subject: [PATCH 1/2] add frontend_trigger test --- Makefile | 10 +- configs/frontend_trigger.yaml | 88 + pyproject.toml | 21 + .../ifu/frontend_trigger/FrontendTrigger.fst | Bin 0 -> 243318 bytes .../VFrontendTrigger_coverage.dat | 3167 +++++++++++++++++ .../ifu/frontend_trigger/agent/__init__.py | 2 +- .../agent/frontend_trigger_agent.py | 242 +- .../ifu/frontend_trigger/test/common_task.py | 135 + .../ifu/frontend_trigger/test/demo_test.py | 67 +- .../test/frontend_trigger_fixture.py | 50 +- .../test/frontend_trigger_ref.py | 352 ++ .../test/frontend_trigger_tools.py | 242 ++ .../test/test_bug_examples.py | 107 + .../ifu/frontend_trigger/test/test_chain.py | 327 ++ .../test/test_normal_match.py | 156 + .../test/test_normal_no_match.py | 67 + uv.lock | 406 +++ 17 files changed, 5385 insertions(+), 54 deletions(-) create mode 100644 configs/frontend_trigger.yaml create mode 100644 pyproject.toml create mode 100644 ut_frontend/ifu/frontend_trigger/FrontendTrigger.fst create mode 100644 ut_frontend/ifu/frontend_trigger/VFrontendTrigger_coverage.dat create mode 100644 ut_frontend/ifu/frontend_trigger/test/common_task.py create mode 100644 ut_frontend/ifu/frontend_trigger/test/frontend_trigger_ref.py create mode 100644 ut_frontend/ifu/frontend_trigger/test/frontend_trigger_tools.py create mode 100644 ut_frontend/ifu/frontend_trigger/test/test_bug_examples.py create mode 100644 ut_frontend/ifu/frontend_trigger/test/test_chain.py create mode 100644 ut_frontend/ifu/frontend_trigger/test/test_normal_match.py create mode 100644 ut_frontend/ifu/frontend_trigger/test/test_normal_no_match.py create mode 100644 uv.lock diff --git a/Makefile b/Makefile index 4c6f72a3..055d829b 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,15 @@ test_all: dut @python3 run.py --config $(CFG) $(KV) -- $(REPORT) -vs ut_*/ $(args) test: dut - @python3 run.py --config $(CFG) $(KV) -- $(REPORT) -vs $(target) $(args) + python3 run.py --config $(CFG) $(KV) -- $(REPORT) -vs $(target) $(args) -n auto + +test_trigger: dut + make test CFG="./configs/frontend_trigger.yaml" target="./ut_frontend/ifu/frontend_trigger/" + + + +test_predecode: dut + make test target="./ut_frontend/ifu/predecode/" dut: rtl @python3 run.py --config $(CFG) --build $(DUTS) $(args) diff --git a/configs/frontend_trigger.yaml b/configs/frontend_trigger.yaml new file mode 100644 index 00000000..d322967f --- /dev/null +++ b/configs/frontend_trigger.yaml @@ -0,0 +1,88 @@ +#coding=utf8 +#*************************************************************************************** +# This project 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. +#**************************************************************************************/ + +# Default configuration file +# Configuration load order: _default.yaml -> user-specified *.yaml -> command line arguments e.g., log.term-level='debug' + +# RTL configuration +rtl: + # RTL download URL, fetch all *.gz.tar files from this URL as target RTL + base-url: https://raw.githubusercontent.com/XS-MLVP/UnityChipXiangShanRTLs/refs/heads/main/README.md + # RTL version to download e.g., openxiangshan-kmh-97e37a2237-24092701 + version: latest + # Directory to store RTL, relative to the current configuration file path + cache-dir: "%{root}/rtl" + +# Test case configuration (tags and cases support wildcards) +test: + # Skip tags, all test cases with these tags will be skipped + skip-tags: ["LONG_TIME_RUN", "RARELY_USED", "REGRESSION", "CI","BUG"] + # Run tags, only test cases with these tags will be executed (skip-tags override run-tags) + run-tags: [] + # Skip cases, all test cases with these names (or module names) will be skipped + skip-cases: [] + # Run cases, only test cases with these names (or module names) will be executed (skip-cases override run-cases) + run-cases: [] + # Skip exceptions, all test cases that raise these exceptions will be skipped + skip-exceptions: [] + +# Output configuration +output: + # Output directory, relative to the current configuration file path + out-dir: "%{root}/out" + +# Test report configuration +report: + # Report generation directory, relative to output.out-dir + report-dir: "report" + # Report name, supports variable substitution: %{host} hostname, %{pid} process ID, %{time} current time + report-name: "%{time}-%{gitag}/index.html" + # Report content + information: + # Report title + title: "XiangShan KMH Test Report" + # Report user information + user: + name: "leesum" + email: "leesum1@qq.com" + # Report user information + line_grate: 99 + # Other information to display, key is the title, value is the content + meta: + Version: "%{giturl}" + +# Log configuration +log: + # Root log level + root-level: "debug" + # Terminal log level + term-level: "info" + # Directory to store log files, relative to the outout.out-dir path + file-dir: "log" + # Log file name, supports variable substitution: %{host} hostname, %{pid} process ID, %{time} current time + file-name: "%{host}-%{pid}-%{time}.log" + # File log level + file-level: "info" + +# Doc result configuration (this data is used to populate charts and other statistics in +# the documents, and the raw data comes from the report generated by toffee-test). +# After run test, use `make doc` to check the result. +doc-result: + # Process result or not + disable: False + # Target duts and their structure + dutree: "%{root}/configs/dutree/xiangshan-kmh.yaml" + # Result path, relative to the current configuration file path + result-name: "ut_data_progress.json" + report-link: "%{root}/documents/static/data/reports" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..0576ab5c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,21 @@ +[project] +name = "unitychipforxiangshan" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [ + "allure-pytest>=2.13.5", + "filelock>=3.9.0", + "hypothesis>=6.124.7", + "pytest>=8.3.4", + "pytest-asyncio>=0.25.2", + "pytoffee", + "pyyaml>=6.0.1", + "requests>=2.28.1", + "toffee-test", +] + +[tool.uv.sources] +pytoffee = { git = "https://github.com/XS-MLVP/toffee", rev = "master" } +toffee-test = { git = "https://github.com/XS-MLVP/toffee-test", rev = "master" } diff --git a/ut_frontend/ifu/frontend_trigger/FrontendTrigger.fst b/ut_frontend/ifu/frontend_trigger/FrontendTrigger.fst new file mode 100644 index 0000000000000000000000000000000000000000..261a8ad6a27fc8950b6b4a2ac34fee64c05abee0 GIT binary patch literal 243318 zcmce;d3+RA_J@6Lb%iuBLM7E;3lW+au+@yyK*WFu0RlutYzS&V2O=P5kkJT?Mnx?l zYE;x{5F?-jaEZ7D6$f`zRK^7n!NHwzMaLxyDz5WB=XM9dnfbl%U$1_WbXVQF_w47~ z^R0`B{m&eo`rG!qA17bjEy*LaxB(2)~D})KK%zA&WA*w!?XT>q9P@v&>da&Uaw;~-=JhWm;XiDh3B3ho-jl@ zhqD~%@#8+3lG$D&@8`;abF;gO_gPHDkpsTzDj9>N{+oTmS@!?&FNoxpgoE(fpq+%< z%Yi#HawSsRRU&Kn`*VBANImd#hJ@`fOMCBDM=Bi&dtqs6=hg9~SDPWZJYTXk!&}l( zHqLJ+p59o{&hunm=mA-`r0;tdp0(p|ju=@G7E|=x9ZmON1ch_?ncrTXxdsBv=La44 z*?H}lCz8icH!gSKvUKm8yYkyndO69@ST2|B%n5aJb^aw>u3eKMq4Qq*>XTh(>g5`K&S@`A=fjm*bm*|UAEt@n+H`&#W|0{Z`c9-tBq|b!Fgr{BC=wAf!%ntQ z^ocyal!&a=^=4*sp~c8wL>?Bu;8~HQM23r>^dzTXu#tR`=SBV|e#v{1pYUE2*~iEf zktgNsgk-mRl6`@6a*^GM5<+UMmnSk^YC=~#a<%aX-sectN~GA%wc3&A9Tm_$PMR1NU>R5Z;x`=0 zaAaRcd3s2d=T&}ddPw+sBc-CVFd!;^)oqS^DN-#tMNb=N-;k1sXf~>%0D(F}*#BE*CjRq>spYBS(hhdXbru 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T!yX_nH8R-GyV*U*G8Ce*F+D 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 NWZmQ list[BreakpointInfo]: - self.bundle.io._frontendTrigger._tUpdate._bits._addr.value = pos - self.bundle.io._frontendTrigger._tUpdate._bits._tdata._tdata2.value = req.data - self.bundle.io._frontendTrigger._tUpdate._bits._tdata._chain.value = req.chain - self.bundle.io._frontendTrigger._tUpdate._bits._tdata._matchType.value = req.matchType - self.bundle.io._frontendTrigger._tUpdate._bits._tdata._select.value = req.select - getattr(self.bundle.io._frontendTrigger._tEnableVec, f"_{pos}").value = enable - self.bundle.io._frontendTrigger._triggerCanRaiseBpExp.value = _raise + async def set_breakpoint_update( + self, + addr: int, + bp_update: BreakpointUpdateInfo, + bp_flags: Optional[BreakpointFlags] = None, + ): + + assert addr < 4, "pos must be less than 4" + + self.bundle.io._frontendTrigger._tUpdate._bits._addr.value = addr + self.bundle.io._frontendTrigger._tUpdate._bits._tdata._tdata2.value = ( + bp_update.tdata2 + ) + self.bundle.io._frontendTrigger._tUpdate._bits._tdata._chain.value = ( + bp_update.chain + ) + self.bundle.io._frontendTrigger._tUpdate._bits._tdata._matchType.value = ( + bp_update.matchType + ) + self.bundle.io._frontendTrigger._tUpdate._bits._tdata._select.value = ( + bp_update.select + ) + self.bundle.io._frontendTrigger._tUpdate._bits._tdata._action.value = ( + bp_update.action + ) + + if bp_flags is not None: + for i in range(4): + getattr(self.bundle.io._frontendTrigger._tEnableVec, f"_{i}").value = ( + bp_flags.tEnableVec[i] + ) + self.bundle.io._frontendTrigger._debugMode.value = bp_flags.debugMode + self.bundle.io._frontendTrigger._triggerCanRaiseBpExp.value = ( + bp_flags.triggerCanRaiseBpExp + ) self.bundle.io._frontendTrigger._tUpdate._valid.value = True - await self.bundle.step(2) + await self.bundle.step() + self.bundle.io._frontendTrigger._tUpdate._valid.value = False + await self.bundle.step() - res = [] + @driver_method() + async def set_breakpoint_flags( + self, + bp_flags: BreakpointFlags, + ): + for i in range(4): + getattr(self.bundle.io._frontendTrigger._tEnableVec, f"_{i}").value = ( + bp_flags.tEnableVec[i] + ) + self.bundle.io._frontendTrigger._debugMode.value = bp_flags.debugMode + self.bundle.io._frontendTrigger._triggerCanRaiseBpExp.value = ( + bp_flags.triggerCanRaiseBpExp + ) + await self.bundle.step(1) + + @monitor_method() + async def monitor_breakpoint_update(self): + if self.bundle.io._frontendTrigger._tUpdate._valid.value: + await self.bundle.step(1) + return self.collect_triggered() + + @monitor_method() + async def monitor_breakpoint_flags(self): + cur_bp_flags = BreakpointFlags() + cur_bp_flags.tEnableVec = [ + getattr(self.bundle.io._frontendTrigger._tEnableVec, f"_{i}").value + for i in range(4) + ] + cur_bp_flags.debugMode = ( + self.bundle.io._frontendTrigger._tEnableVec._debugMode.value + ) + cur_bp_flags.triggerCanRaiseBpExp = ( + self.bundle.io._frontendTrigger._tEnableVec._triggerCanRaiseBpExp.value + ) + bp_flags_changed = cur_bp_flags != self.old_bp_flags + + if bp_flags_changed: + info(f"bp_flags changed: {self.old_bp_flags} -> {cur_bp_flags}") + self.old_bp_flags = cur_bp_flags + + if bp_flags_changed: + return self.collect_triggered() + + def collect_breakpoint_info(self) -> list[BreakpointInfo]: + res = [] for i in range(4): bp_res = BreakpointInfo() - bp_res.timing = getattr(self.bundle.FrontendTrigger._tdataVec, f"_{i}")._timing.value - bp_res.tdata2 = getattr(self.bundle.FrontendTrigger._tdataVec, f"_{i}")._tdata2.value - bp_res.chain = getattr(self.bundle.FrontendTrigger._tdataVec, f"_{i}")._chain.value - bp_res.matchType = getattr(self.bundle.FrontendTrigger._tdataVec, f"_{i}")._matchType.value - bp_res.select = getattr(self.bundle.FrontendTrigger._tdataVec, f"_{i}")._select.value - bp_res.action = getattr(self.bundle.FrontendTrigger._tdataVec, f"_{i}")._action.value + bp_res.timing = getattr( + self.bundle.FrontendTrigger._tdataVec, f"_{i}" + )._timing.value + bp_res.tdata2 = getattr( + self.bundle.FrontendTrigger._tdataVec, f"_{i}" + )._tdata2.value + bp_res.chain = getattr( + self.bundle.FrontendTrigger._tdataVec, f"_{i}" + )._chain.value + bp_res.matchType = getattr( + self.bundle.FrontendTrigger._tdataVec, f"_{i}" + )._matchType.value + bp_res.select = getattr( + self.bundle.FrontendTrigger._tdataVec, f"_{i}" + )._select.value + bp_res.action = getattr( + self.bundle.FrontendTrigger._tdataVec, f"_{i}" + )._action.value res.append(bp_res) return res - + + @monitor_method() + async def monitor_pcs_changed(self): + # Fetch current PC values + cur_pcs = [getattr(self.bundle.io._pc, f"_{i}").value for i in range(16)] + + pcs_changed = cur_pcs != self.old_pcs + + self.old_pcs = cur_pcs + if pcs_changed: + cur_triggered = self.collect_triggered() + return cur_triggered + + def collect_triggered(self) -> list[int]: + ret = [] + for i in range(16): + ret.append(getattr(self.bundle.io._triggered, f"_{i}").value) + return ret + # param: pcs: 16 x pc - # return: triggered?s: 16 x triggered @driver_method() - async def check(self, pcs: list[int]) -> list[int]: + async def set_pcs(self, pcs: list[int]) -> list[int]: + # 输入验证 + assert len(pcs) == 16, "pcs must contain exactly 16 elements" + assert pcs[0] >= 0, "The first element of pcs cannot be negative" + for i in range(1, 16): + assert ( + pcs[i] == pcs[i - 1] + 2 + ), f"pcs[{i}] ({pcs[i]}) is not exactly 2 greater than pcs[{i - 1}] ({pcs[i - 1]})" + num_instrs = 16 for i in range(num_instrs): - getattr(self.bundle.io._pc, f"_{i}").value = pcs[i] + cur_pc = getattr(self.bundle.io._pc, f"_{i}") + cur_pc.value = pcs[i] await self.bundle.step() - ret = [] + def get_old_pcs(self) -> list[int]: + return self.old_pcs - for i in range(num_instrs): - ret.append(getattr(self.bundle.io._triggered, f"_{i}").value) - - return ret \ No newline at end of file + @driver_method() + async def send_cycle(self, new_cycle: int): + self.cycle = new_cycle + await self.bundle.step() + # BUG: driver_method 有返回值时,不应该用于 ref model 的比对 + return self.cycle + 1 diff --git a/ut_frontend/ifu/frontend_trigger/test/common_task.py b/ut_frontend/ifu/frontend_trigger/test/common_task.py new file mode 100644 index 00000000..971ab10d --- /dev/null +++ b/ut_frontend/ifu/frontend_trigger/test/common_task.py @@ -0,0 +1,135 @@ +from itertools import islice +import random +from ut_frontend.ifu.frontend_trigger.agent.frontend_trigger_agent import ( + BreakpointFlags, + BreakpointUpdateInfo, + FrontendTriggerAgent, +) +from ut_frontend.ifu.frontend_trigger.test.frontend_trigger_tools import ( + bound_pair_generator, + gen_pcs, + to_even, +) + + +async def ticks_task(agent: FrontendTriggerAgent, max_ticks=None): + """ + 手动对时钟信号进行计数, 并且用于驱动 refmodel + """ + counter = 0 + while max_ticks is None or counter < max_ticks: + await agent.send_cycle(counter) + counter += 1 + + +async def send_exit(agent: FrontendTriggerAgent): + """ + 当测试结束时,通知 refmodel 停止仿真 + """ + await agent.reset() + + +async def ftrigger_common_task1( + agent: FrontendTriggerAgent, + bp_update_gen, + bp_flags_gen, + pc_gen, + max_task_loop=100, + max_pcs_loop_per_task=200, +): + + task_count = 0 + trigger_info_list: list[BreakpointUpdateInfo] = [] # 存储断点更新信息的列表 + + while task_count < max_task_loop: + task_count += 1 + trigger_info_list.clear() + # 生成触发PC列表 + trigger_pc_list = gen_pcs([next(pc_gen)]) + trigger_pc_sample = random.sample(trigger_pc_list, 4) + # 配置每个断点的更新信息 + for i in range(4): + bp_update = next(bp_update_gen) + bp_update.tdata2 = trigger_pc_sample[i] + trigger_info_list.append(bp_update) + await agent.set_breakpoint_update(i, bp_update) + # 设置断点标志 + await agent.set_breakpoint_flags(next(bp_flags_gen)) + + # 随机生成PC值并验证触发逻辑 + for _ in range(max_pcs_loop_per_task): + trigger_pcs = [x.tdata2 for x in trigger_info_list] + sample_count = random.randint(0, 4) + trigger_pcs_sample = [next(pc_gen)] + if sample_count > 0: + trigger_pcs_sample = random.sample(trigger_pcs, sample_count) + + rand_pcs = gen_pcs(trigger_pcs_sample) + assert len(rand_pcs) == 16, "rand_pcs 的长度必须为 16" + + if rand_pcs == agent.get_old_pcs(): + continue + + await agent.set_pcs(rand_pcs) + + +async def ftrigger_chain_task1( + agent: FrontendTriggerAgent, + first_chain_bp_update_gen, + second_chain_bp_update_gen, + bp_flags_gen, + pc_gen, + max_task_loop=100, +): + task_count = 0 + + while task_count < max_task_loop: + task_count += 1 + + # 生成 pcs + pcs_list = gen_pcs([next(pc_gen)]) + cur_task_bp_flags = next(bp_flags_gen) + + if pcs_list == agent.get_old_pcs(): + continue + + await agent.set_pcs(pcs_list) + + # enable 0 and 1 breakpoints + await agent.set_breakpoint_flags(cur_task_bp_flags) + + chain_trigger_pc_sample = random.choice(pcs_list) + match_type_map = {">=": 2, "==": 0, "<": 3} + + # BUG: 因为 < 有 bug , 所以只生成 >= 和 == 的 bound + bound_pair_gen = bound_pair_generator(chain_trigger_pc_sample, [">=", "=="]) + + # 生成 20 个 unique 的 bound, 不足 20 也没关系 + bound_pair_list = list(set(islice(bound_pair_gen, 20))) + + for bound1, bound2 in bound_pair_list: + # first_chain_bp = BreakpointUpdateInfo( + # matchType=match_type_map[bound1[0]], + # select=0, + # action=random.randint(0, 1), + # chain=True, + # tdata2=to_even(bound1[1]), + # ) + # second_chain_bp = BreakpointUpdateInfo( + # matchType=match_type_map[bound2[0]], + # select=0, + # action=random.randint(0, 1), + # chain=False, + # tdata2=to_even(bound2[1]), + # ) + first_chain_bp = next(first_chain_bp_update_gen) + second_chain_bp = next(second_chain_bp_update_gen) + + first_chain_bp.tdata2 = to_even(bound1[1]) + first_chain_bp.matchType = match_type_map[bound1[0]] + second_chain_bp.tdata2 = to_even(bound2[1]) + second_chain_bp.matchType = match_type_map[bound2[0]] + + # 配置链式断点, 并且验证触发逻辑 + await agent.set_breakpoint_update(0, first_chain_bp) + await agent.set_breakpoint_update(1, second_chain_bp) diff --git a/ut_frontend/ifu/frontend_trigger/test/demo_test.py b/ut_frontend/ifu/frontend_trigger/test/demo_test.py index fd1e1ef4..8e7f911b 100644 --- a/ut_frontend/ifu/frontend_trigger/test/demo_test.py +++ b/ut_frontend/ifu/frontend_trigger/test/demo_test.py @@ -1,24 +1,65 @@ +import random import toffee_test +from toffee import Executor + +from ut_frontend.ifu.frontend_trigger.agent.frontend_trigger_agent import ( + BreakpointFlags, + BreakpointUpdateInfo, + FrontendTriggerAgent, +) +from ut_frontend.ifu.frontend_trigger.test.common_task import ( + ftrigger_common_task1, + send_exit, + ticks_task, +) +from ut_frontend.ifu.frontend_trigger.test.frontend_trigger_tools import ( + bp_flags_generator, + bp_update_generator, + gen_pcs, + get_mask_one, + int_generator, +) from .frontend_trigger_fixture import frontend_trigger_env from ..env import FrontendTriggerEnv -from ..agent import BreakpointReq + @toffee_test.testcase async def test_smoke(frontend_trigger_env: FrontendTriggerEnv): await frontend_trigger_env.agent.reset() - bp_req = BreakpointReq() - bp_req.chain = False - bp_req.data = 121213242 - bp_req.matchType = 0 - bp_req.select = 0 + async def __smoke_task1(): + bp_update = BreakpointUpdateInfo() + bp_update.chain = False + bp_update.matchType = 3 + bp_update.select = 0 + bp_update.action = 0 + bp_update.tdata2 = 0x1234_1234_5678 + + start = 0x1234_1234_5670 + pcs = [start + i * 2 for i in range(16)] + + bp_flags = BreakpointFlags() + bp_flags.debugMode = False # TODO: 搞清楚这个参数的含义 + bp_flags.triggerCanRaiseBpExp = True + bp_flags.tEnableVec = [True, False, False, False] + + await frontend_trigger_env.agent.set_breakpoint_update( + 0, bp_update, bp_flags=bp_flags + ) - bp_infos = await frontend_trigger_env.agent.set_breakpoint(0, bp_req) - # bp_infos = await frontend_trigger_env.agent.set_breakpoint(2, bp_req) - for i in range(4): - print(bp_infos[i]) + await frontend_trigger_env.agent.bundle.step(2) + await frontend_trigger_env.agent.set_pcs(pcs) + await frontend_trigger_env.agent.bundle.step(2) + # await frontend_trigger_env.agent.set_pcs(list(map(lambda x: x + 2, pcs))) + # await frontend_trigger_env.agent.bundle.step(2) + # await frontend_trigger_env.agent.set_pcs(list(map(lambda x: x - 2, pcs))) + # bp_flags.tEnableVec = [True, True, True, True] + # await frontend_trigger_env.agent.set_breakpoint_flags(bp_flags) + # await frontend_trigger_env.agent.bundle.step(2) - start=121213230 - pcs = [start+i*2 for i in range(16)] + async with Executor(exit="any") as exec: + exec(ticks_task(frontend_trigger_env.agent)) + exec(__smoke_task1()) - print(await frontend_trigger_env.agent.check(pcs)) \ No newline at end of file + # notify the refmodel to stop the simulation + await send_exit(frontend_trigger_env.agent) diff --git a/ut_frontend/ifu/frontend_trigger/test/frontend_trigger_fixture.py b/ut_frontend/ifu/frontend_trigger/test/frontend_trigger_fixture.py index 8d10c566..f73daae7 100644 --- a/ut_frontend/ifu/frontend_trigger/test/frontend_trigger_fixture.py +++ b/ut_frontend/ifu/frontend_trigger/test/frontend_trigger_fixture.py @@ -1,20 +1,66 @@ import toffee_test import toffee +from comm.functions import module_name_with from dut.FrontendTrigger import DUTFrontendTrigger +from ut_frontend.ifu.frontend_trigger.test.frontend_trigger_ref import ( + BpRefModel, +) +from comm.functions import UT_FCOV, module_name_with +import toffee.funcov as fc + + from ..env import FrontendTriggerEnv + +gr = fc.CovGroup(UT_FCOV("../../TOFFEE"), disable_sample_when_point_hinted=False) + + +def init_frontend_trigger_funcov(dut: DUTFrontendTrigger, g: fc.CovGroup): + """Add watch points to the RVCExpander module to collect function coverage information""" + + for i in range(16): + g.add_watch_point( + dut, + { + "BKPT_EXCPT": lambda d: getattr(dut, f"io_triggered_{i}").value == 0, + "DEBUG_MODE": lambda d: getattr(dut, f"io_triggered_{i}").value == 1, + }, + name=f"PC{i}_TRIGGERED", + ) + + # Reverse mark function coverage to the check point + # TODO: 检查为什么无效 + for i in range(16): + g.mark_function( + f"PC{i}_TRIGGERED", + func=[ + module_name_with("test_match_eq", "./test_normal_match"), + module_name_with("test_match_ge", "./test_normal_match"), + module_name_with("test_match_lt", "./test_normal_match"), + ], + bin_name=["BKPT_EXCPT", "DEBUG_MODE"], + ) + + @toffee_test.fixture async def frontend_trigger_env(toffee_request: toffee_test.ToffeeRequest): - toffee.setup_logging(toffee.WARNING) + toffee.setup_logging(toffee.INFO, log_file="toffee.log") dut = toffee_request.create_dut(DUTFrontendTrigger) # toffee_request.add_cov_groups(pred_checker_cover_point(dut)) dut.InitClock("clock") toffee.start_clock(dut) + + init_frontend_trigger_funcov(dut, gr) + toffee_request.add_cov_groups([gr]) + env = FrontendTriggerEnv(dut) + env.attach(BpRefModel()) + # await env.agent.reset() yield env import asyncio + cur_loop = asyncio.get_event_loop() for task in asyncio.all_tasks(cur_loop): if task.get_name() == "__clock_loop": @@ -22,4 +68,4 @@ async def frontend_trigger_env(toffee_request: toffee_test.ToffeeRequest): try: await task except asyncio.CancelledError: - break \ No newline at end of file + break diff --git a/ut_frontend/ifu/frontend_trigger/test/frontend_trigger_ref.py b/ut_frontend/ifu/frontend_trigger/test/frontend_trigger_ref.py new file mode 100644 index 00000000..ed30cbe3 --- /dev/null +++ b/ut_frontend/ifu/frontend_trigger/test/frontend_trigger_ref.py @@ -0,0 +1,352 @@ +from toffee.logger import * +from toffee.triggers import * +from toffee.model import * +from dataclasses import dataclass +from typing import List, Optional + +from ut_frontend.ifu.frontend_trigger.agent.frontend_trigger_agent import ( + BreakpointFlags, + BreakpointUpdateInfo, +) + + +@dataclass +class RefTriggerReg: + tdata2: int + chain: bool + timing: bool + matchType: int + select: int + action: int + + def __init__(self): + self.tdata2 = 0 + self.chain = False + self.timing = False + self.matchType = 0 + self.select = 0 + self.action = 0 + + def __str__(self): + return f"tdata2: {self.tdata2}; chain: {self.chain}; timing: {self.timing}; matchType: {self.matchType}; select: {self.select}; action: {self.action}" + + +class BpRefImpl: + def __init__(self): + # bp_flags + self.bp_flags = BreakpointFlags() + + # trigger_regs + self.trigger_addr2idx = [0, 1, 6, 8] + self.trigger_idx2addr = {0: 0, 1: 1, 6: 2, 8: 3} + self.trigger_regs = [RefTriggerReg() for _ in self.trigger_addr2idx] + + # pcs + self.pcs = [0] * 16 + + @staticmethod + def ref_match_cmp(pc: int, trigger_reg: int, match_type: int) -> bool: + if match_type == 0: + # equal + return pc == trigger_reg + elif match_type == 2: + # greater or equal + return pc >= trigger_reg + elif match_type == 3: + # less than + return pc < trigger_reg + else: + raise ValueError(f"Invalid match type:{match_type}") + + def ref_update_trigger_regs(self, addr: int, bp_update: BreakpointUpdateInfo): + assert addr < 4, "pos must be less than 4" + + self.trigger_regs[addr].tdata2 = bp_update.tdata2 + self.trigger_regs[addr].chain = bp_update.chain + self.trigger_regs[addr].timing = False + self.trigger_regs[addr].matchType = bp_update.matchType + self.trigger_regs[addr].select = bp_update.select + self.trigger_regs[addr].action = bp_update.action + + def ref_update_bp_flags(self, bp_flags: BreakpointFlags): + self.bp_flags = bp_flags + + def ref_update_pcs(self, pcs: list[int]): + # len(pcs) == 16 + assert len(pcs) == 16, "pcs length must be 16" + self.pcs = pcs + + def ref_check_trigger(self) -> list[int]: + # 16 * 4 的矩阵,记录每个pc在每个trigger上的触发情况(不检查 chain) + pc_trigger_matrix = self.get_trigger_matrix() + + final_pc_trigger_list = [] + for pc_idx, pc_trigger_list in enumerate(pc_trigger_matrix): + final_action = self.get_priority_action(pc_trigger_list) + final_pc_trigger_list.append(final_action) + + assert ( + len(final_pc_trigger_list) == 16 + ), "final_pc_trigger_list length must be 16" + return final_pc_trigger_list + + def is_trigger_enabled(self, trigger_idx: int) -> bool: + trigger_addr = self.trigger_idx2addr[trigger_idx] + return ( + # enable + self.bp_flags.tEnableVec[trigger_addr] + # select must be 0 + and self.trigger_regs[trigger_addr].select == 0 + # debugMode must be False + and self.bp_flags.debugMode == False + ) + + def get_action_from_trigger(self, trigger_idx: int) -> int: + """ + Args: + trigger_idx (int): trigger 在 spec 中的索引 + """ + trigger_addr = self.trigger_idx2addr[trigger_idx] + if ( + self.trigger_regs[trigger_addr].action == 0 + and self.bp_flags.triggerCanRaiseBpExp + ): + return 0 + elif self.trigger_regs[trigger_addr].action == 1: + return 1 + else: + return 15 + + def get_trigger_matrix(self) -> list[list[int]]: + # 初始化每个pc的触发情况矩阵(二维列表) + # More than one triggers can hit at the same time, but only fire one. + # We select the first hit trigger to fire. + num_pcs = 16 + num_triggers = 4 + + # 16 * 4 的二维矩阵 + pc_trigger_matrix = [[15] * num_triggers for _ in range(num_pcs)] + + for t_reg_idx, t_reg in zip(self.trigger_addr2idx, self.trigger_regs): + t_reg_addr = self.trigger_idx2addr[t_reg_idx] + if not self.is_trigger_enabled(t_reg_idx): + continue + + for pc_idx, pc in enumerate(self.pcs): + if self.ref_match_cmp(pc, t_reg.tdata2, t_reg.matchType): + # 记录触发状态到矩阵 + cur_action = self.get_action_from_trigger(t_reg_idx) + pc_trigger_matrix[pc_idx][t_reg_addr] = cur_action + + return pc_trigger_matrix + + # def get_priority_action(self, pc_trigger_list: list[int]) -> int: + # """ + # 从一个pc在每个trigger上的触发情况中获取优先级最高的触发动作, 并且处理chain trigger 的情况 + + # More than one triggers can hit at the same time, but only fire one. + # We select the first hit trigger to fire. + # Args: + # pc_trigger_list (list[int]): 一个pc在每个trigger上的触发情况 + + # Returns: + # int: 优先级最高的触发动作 + # """ + # tmp_chain_group = [] + # for t_addr, t_action in enumerate(pc_trigger_list): + # cur_t_idx = self.trigger_addr2idx[t_addr] + # cur_chain = self.trigger_regs[t_addr].chain + # cur_timing = self.trigger_regs[t_addr].timing + # cur_action = t_action + + # if len(tmp_chain_group) == 0 or tmp_chain_group[-1]["t_idx"] == ( + # cur_t_idx - 1 + # ): + # # 1. 第一个trigger没有前一个trigger + # # 2. 当前trigger 的 idx 是前一个trigger 的 idx - 1, 可能是chain + # tmp_chain_group.append( + # { + # "chain": cur_chain, + # "timing": cur_timing, + # "action": cur_action, + # "t_idx": cur_t_idx, + # } + # ) + # else: + # # 3. 当前trigger 的 idx 不是前一个trigger 的 idx - 1, 不是chain + # # (a) 清空当前的 chain group, chain 设置为 True 必然不触发 + # # (b) 将当前trigger加入新的 chain group + # tmp_chain_group.clear() + # tmp_chain_group.append( + # { + # "chain": cur_chain, + # "timing": cur_timing, + # "action": cur_action, + # "t_idx": cur_t_idx, + # } + # ) + + # # 如果当前trigger不是chain + # # 1. 如果当前chain group中的trigger数量大于1, 是 chain 的最后一个 + # # 2. 如果当前chain group中的trigger数量等于1, 是单独的一个 trigger + # if tmp_chain_group[-1]["chain"] == False: + # # chain trigger 的 timing 必须相同 + # all_timing_equal = all( + # [ + # x["timing"] == tmp_chain_group[0]["timing"] + # for x in tmp_chain_group + # ] + # ) + # # chain trigger 需要所有的 trigger 都 match + # all_action_match = all([x["action"] != 15 for x in tmp_chain_group]) + # if all_timing_equal and all_action_match: + # return tmp_chain_group[-1]["action"] + + # tmp_chain_group.clear() + + # return 15 + + def get_priority_action(self, pc_trigger_list: list[int]) -> int: + tmp_chain_group = [] + for t_addr, t_action in enumerate(pc_trigger_list): + cur_t_idx = self.trigger_addr2idx[t_addr] + cur_chain = self.trigger_regs[t_addr].chain + cur_timing = self.trigger_regs[t_addr].timing + cur_action = t_action + + # 确定当前触发器是否可加入现有链组 + if tmp_chain_group: + last_entry = tmp_chain_group[-1] + # 检查索引连续性和时序一致性 + if ( + cur_t_idx != last_entry["t_idx"] + 1 + or cur_timing != tmp_chain_group[0]["timing"] + ): + tmp_chain_group.clear() + + # 将当前触发器加入链组(无论是否清空后) + tmp_chain_group.append( + { + "chain": cur_chain, + "action": cur_action, + "t_idx": cur_t_idx, + "timing": cur_timing, + } + ) + + # 处理非链式触发器作为链尾的情况 + if not cur_chain: + # 所有动作有效时触发 + if all(x["action"] != 15 for x in tmp_chain_group): + return cur_action + tmp_chain_group.clear() + + # 所有触发器检查完毕仍未找到有效动作 + return 15 + + +class BpRefModel(Model): + def __init__(self): + super().__init__() + + # 初始化BpRefImpl实例 + self.impl = BpRefImpl() + + # cycle驱动端口 + self.cycle_driver = DriverPort(agent_name="agent", driver_name="send_cycle") + + # reset 端口 + self.reset_driver = DriverPort(agent_name="agent", driver_name="reset") + + # 驱动端口 + self.bp_update_driver = DriverPort( + agent_name="agent", driver_name="set_breakpoint_update" + ) + self.bp_flags_driver = DriverPort( + agent_name="agent", driver_name="set_breakpoint_flags" + ) + self.pcs_driver = DriverPort(agent_name="agent", driver_name="set_pcs") + + # 监视端口 + self.bp_update_monitor = MonitorPort( + agent_name="agent", monitor_name="monitor_breakpoint_update" + ) + self.bp_flags_monitor = MonitorPort( + agent_name="agent", monitor_name="monitor_breakpoint_flags" + ) + self.pcs_monitor = MonitorPort( + agent_name="agent", monitor_name="monitor_pcs_changed" + ) + + async def get_bp_update_driv(self): + dict_res = await self.bp_update_driver() + addr: int = dict_res["addr"] + bp_update: BreakpointUpdateInfo = dict_res["bp_update"] + bp_flags: Optional[BreakpointFlags] = dict_res["bp_flags"] + return addr, bp_update, bp_flags + + async def get_bp_flags_driv(self) -> BreakpointFlags: + dict_res = await self.bp_flags_driver() + # 确保类型正确 + assert isinstance(dict_res, BreakpointFlags), "bp_flags type error" + return dict_res + + async def get_pcs_driv(self) -> list[int]: + dict_res = await self.pcs_driver() + # print(f'get_pcs_driv: {dict_res}') + return dict_res + + async def main(self): + + await self.reset_driver() + info("reset ok") + while True: + cur_cycle = await self.cycle_driver() + + # 处理断点更新 + if not self.bp_update_driver.empty(): + addr, bp_update, bp_flags = await self.get_bp_update_driv() + + self.impl.ref_update_trigger_regs(addr, bp_update) + if bp_flags is not None: + self.impl.ref_update_bp_flags(bp_flags) + info( + f"cycle[{cur_cycle}][bp_update] addr: {addr}, bp_update: {bp_update}, bp_flags: {bp_flags}" + ) + trigger_res = self.impl.ref_check_trigger() + info(f"cycle[{cur_cycle}]trigger_res: {trigger_res}") + + await self.bp_update_monitor.put(trigger_res) + + # 处理标志更新 + if not self.bp_flags_driver.empty(): + bp_flags = await self.get_bp_flags_driv() + self.impl.ref_update_bp_flags(bp_flags) + info(f"cycle[{cur_cycle}][bp_flags] {bp_flags}") + trigger_res = self.impl.ref_check_trigger() + info(f"cycle[{cur_cycle}]trigger_res: {trigger_res}") + await self.bp_flags_monitor.put(trigger_res) + + # 处理PCs更新 + if not self.pcs_driver.empty(): + pcs = await self.get_pcs_driv() + old_pcs = self.impl.pcs + old_pcs_hex = [hex(pc) for pc in old_pcs] + pcs_hex = [hex(pc) for pc in pcs] + + info(f"cycle[{cur_cycle}]old_pcs: {old_pcs_hex}") + info(f"cycle[{cur_cycle}]new_pcs: {pcs_hex}") + + self.impl.ref_update_pcs(pcs) + + trigger_res = self.impl.ref_check_trigger() + info(f"cycle[{cur_cycle}]trigger_res: {trigger_res}") + await self.pcs_monitor.put(trigger_res) + + if not self.reset_driver.empty(): + await self.reset_driver() + info(f"cycle[{cur_cycle}]ref exit") + break + + # 确保cycle驱动端口已清空 + assert self.cycle_driver.empty(), "cycle_driver is not empty" diff --git a/ut_frontend/ifu/frontend_trigger/test/frontend_trigger_tools.py b/ut_frontend/ifu/frontend_trigger/test/frontend_trigger_tools.py new file mode 100644 index 00000000..1106438a --- /dev/null +++ b/ut_frontend/ifu/frontend_trigger/test/frontend_trigger_tools.py @@ -0,0 +1,242 @@ +import random +from typing import Callable, Generator, List, Literal, Tuple +from hypothesis import strategies as st +from hypothesis.strategies import composite + +from ut_frontend.ifu.frontend_trigger.agent.frontend_trigger_agent import ( + BreakpointFlags, + BreakpointUpdateInfo, +) + + +def get_mask_one(length: int) -> int: + # 生成一个长度为 length 的全 1 的二进制数,用来进行数据截断 + return (1 << length) - 1 + + +@composite +def bp_update_strategy(draw, pc_width=50): + # Generate a random pc value + max_pc = get_mask_one(pc_width) + pc = draw(st.integers(min_value=0, max_value=max_pc)) + action = draw(st.integers(min_value=0, max_value=1)) + tselect = draw(st.booleans()) + match_type = draw(st.sampled_from([0, 2, 3])) + chain = draw(st.booleans()) + + bp_update = BreakpointUpdateInfo( + action=action, tdata2=pc, select=tselect, matchType=match_type, chain=chain + ) + + return bp_update + + +@composite +def bp_flags_strategy(draw): + tEnableVec = draw(st.lists(st.booleans(), min_size=4, max_size=4)) + debugMode = draw(st.booleans()) + triggerCanRaiseBpExp = draw(st.booleans()) + + return BreakpointFlags( + tEnableVec=tEnableVec, + debugMode=debugMode, + triggerCanRaiseBpExp=triggerCanRaiseBpExp, + ) + + +def bp_update_generator( + pc_width: int = 50, + condition: Callable[[BreakpointUpdateInfo], bool] = lambda x: True, +): + """生成器版本的断点更新信息生成器""" + max_pc = get_mask_one(pc_width - 1) + while True: + # 生成随机pc值 + pc = random.randint(0, max_pc) << 1 + action = random.randint(0, 1) + tselect = random.choice([True, False]) + match_type = random.choice([0, 2, 3]) + chain = random.choice([True, False]) + + bp_update = BreakpointUpdateInfo( + action=action, tdata2=pc, select=tselect, matchType=match_type, chain=chain + ) + + if condition(bp_update): + yield bp_update + + +def bp_flags_generator( + condition: Callable[[BreakpointFlags], bool] = lambda x: True +) -> Generator[BreakpointFlags, None, None]: + while True: + tEnableVec = [random.choice([True, False]) for _ in range(4)] + debugMode = random.choice([True, False]) + triggerCanRaiseBpExp = random.choice([True, False]) + + flags = BreakpointFlags( + tEnableVec=tEnableVec, + debugMode=debugMode, + triggerCanRaiseBpExp=triggerCanRaiseBpExp, + ) + + if condition(flags): + yield flags + + +def int_generator( + min_value: int = 0, + max_value: int = 0xFFFFFFFF, + condition: Callable[[int], bool] = lambda x: True, +) -> Generator[int, None, None]: + while True: + value = random.randint(0, max_value) + if condition(value): + yield value + + +def gen_pcs(include_pc_list): + # 确保 include_pc_list 中的每一项都是偶数,且不为负数 + if any(pc % 2 != 0 or pc < 0 for pc in include_pc_list): + assert False, "include_pc_list 中的每一项必须是偶数且不为负数" + + # 确保最大值和最小值的差不超过 30 + if max(include_pc_list) - min(include_pc_list) > 30: + assert False, "include_pc_list 中的最大项和最小项的差不能超过 30" + + # 计算所有可能的 start 值,使得 rand_pcs 包含 include_pc_list 中的每一项 + min_pc = min(include_pc_list) + max_pc = max(include_pc_list) + + # 计算 start 的范围 + start_min = max(0, max_pc - 2 * 15) + start_max = min_pc + + # 确保为偶数 + start = random.randint(start_min // 2, start_max // 2) * 2 + + # 生成包含 16 项的列表,每项递增 2 + rand_pcs = [start + 2 * i for i in range(16)] + + # 验证 include_pc_list 中的每一项是否在列表中 + for pc in include_pc_list: + assert ( + pc in rand_pcs + ), "生成的列表中未包含 include_pc_list 中的某一项,逻辑错误!" + + return rand_pcs + + +def int_with_condition_generator( + cond_list: List[Tuple[Literal[">", "<", "=", ">=", "<="], int]], + lower_spec: int = 0, + upper_spec: int = int(1e9), +) -> Generator[int, None, None]: + """ + 生成满足所有条件的随机数。 + + Args: + cond_list: 条件列表,每个条件是一个元组 (条件类型, 值)。 + 条件类型可以是 ">", "<", "=", ">=", "<=" 中的一种。 + lower_spec: 指定的下界,在条件列表无解时使用,默认为 0。 + upper_spec: 指定的上界,在条件列表无解时使用,默认为 1e9。 + + Yields: + 生成器,产生满足所有条件的随机整数。 + 如果条件列表无解,则 Yield None 一次,然后停止生成。 + """ + lower_bound = -(10**9) # 初始下界设为负无穷大 + upper_bound = 10**9 # 初始上界设为正无穷大 + + for condition_type, value in cond_list: + if condition_type == ">": + lower_bound = max(lower_bound, value + 1) + elif condition_type == "<": + upper_bound = min(upper_bound, value - 1) + elif condition_type == ">=": + lower_bound = max(lower_bound, value) + elif condition_type == "<=": + upper_bound = min(upper_bound, value) + elif condition_type == "=": + lower_bound = max(lower_bound, value) + upper_bound = min(upper_bound, value) + else: + raise ValueError(f"未知的条件类型: {condition_type}") + + if lower_bound > upper_bound: + print( + "警告: 条件列表互相矛盾,无法找到满足所有条件的数字。将使用指定的上下界范围。" + ) # 增加警告信息 + lower_bound = lower_spec # 使用指定的上下界 + upper_bound = upper_spec + if lower_bound > upper_bound: # 再次检查指定上下界是否有效 + print("警告: 指定的上下界也无效。无法生成随机数。") + yield None # Yield None 表示无法生成 + return # 提前结束生成器 + + if lower_bound > upper_bound: # 最终再次检查,虽然理论上在循环内已经检查过 + print( + "最终检查:条件列表互相矛盾,无法找到满足所有条件的数字。" + ) # 增加最终检查的警告 + yield None # Yield None 表示无法生成 + return # 提前结束生成器 + + while True: + yield random.randint(int(lower_bound), int(upper_bound)) + + +def bound_pair_generator(input_int: int, bound_types: List[Literal[">=", "==", "<"]]): + """ + 生成一个生成器,输出两个 bound,输入的 int 满足该 bound。 + bound 的类型有 >=, ==, < 这三种。 + + Args: + input_int: 输入的整数,生成的 bound 需要满足这个整数。 + + Yields: + 生成器,每次 yield 一对 Bound (Tuple[BoundType, int]), + 其中 BoundType 是 '>=', '==', '<' 中的一种,int 是 bound 的值。 + """ + + def _generate_bound_value(input_int: int, bound_type) -> int: + """ + 根据 bound 类型生成合适的 bound 值,确保 input_int 满足该 bound。 + """ + if bound_type == ">=": + # bound 值应该小于等于 input_int,这样 input_int 才能满足 >= bound_value + return random.randint( + 0, input_int + ) # 范围可以调整,这里使用 input_int - 5 到 input_int + elif bound_type == "==": + # bound 值必须等于 input_int + return input_int + elif bound_type == "<": + # bound 值应该大于 input_int,这样 input_int 才能满足 < bound_value + return random.randint(input_int + 1, input_int * 2) # 范围可以调整 + else: + raise ValueError(f"未知的 bound 类型: {bound_type}") + + while True: + bound1_type = random.choice(bound_types) + bound2_type = random.choice(bound_types) + + bound1_value = _generate_bound_value(input_int, bound1_type) + bound2_value = _generate_bound_value(input_int, bound2_type) + + bound1 = (bound1_type, bound1_value) + bound2 = (bound2_type, bound2_value) + yield (bound1, bound2) + + +def to_odd(x: int) -> int: + """ + 将一个整数转换为奇数。 + """ + return x | 1 + + +def to_even(x: int) -> int: + """ + 将一个整数转换为偶数。 + """ + return x & ~1 diff --git a/ut_frontend/ifu/frontend_trigger/test/test_bug_examples.py b/ut_frontend/ifu/frontend_trigger/test/test_bug_examples.py new file mode 100644 index 00000000..f090fa3c --- /dev/null +++ b/ut_frontend/ifu/frontend_trigger/test/test_bug_examples.py @@ -0,0 +1,107 @@ +import pytest +import toffee_test +from toffee import Executor + +from ut_frontend.ifu.frontend_trigger.agent.frontend_trigger_agent import ( + BreakpointFlags, + BreakpointUpdateInfo, +) +from ut_frontend.ifu.frontend_trigger.env.frontend_trigger_env import FrontendTriggerEnv +from ut_frontend.ifu.frontend_trigger.test.common_task import ( + ticks_task, + send_exit, +) + + +from .frontend_trigger_fixture import frontend_trigger_env + + +@pytest.mark.toffee_tags(["BUG"]) +@toffee_test.testcase +async def test_bug_match_lt(frontend_trigger_env: FrontendTriggerEnv): + """ + 当 matchType 为 3 时(小于), 断点比较逻辑出错 + """ + await frontend_trigger_env.agent.reset() + + async def __smoke_task1(): + bp_update = BreakpointUpdateInfo() + bp_update.chain = False + bp_update.matchType = 3 + bp_update.select = 0 + bp_update.action = 0 + bp_update.tdata2 = 0xDEAD_DEAD_BEE5 + + start = 0xDEAD_DEAD_BEE4 + pcs = [start + i * 2 for i in range(16)] + + bp_flags = BreakpointFlags() + bp_flags.debugMode = False + bp_flags.triggerCanRaiseBpExp = True + bp_flags.tEnableVec = [True, False, False, False] + + await frontend_trigger_env.agent.set_breakpoint_update( + 0, bp_update, bp_flags=bp_flags + ) + + await frontend_trigger_env.agent.bundle.step(2) + await frontend_trigger_env.agent.set_pcs(pcs) + await frontend_trigger_env.agent.bundle.step(2) + + + async with Executor(exit="any") as exec: + exec(ticks_task(frontend_trigger_env.agent)) + exec(__smoke_task1()) + + # notify the refmodel to stop the simulation + await send_exit(frontend_trigger_env.agent) + + +@pytest.mark.toffee_tags(["BUG"]) +@toffee_test.testcase +async def test_chain_no_match_bug(frontend_trigger_env: FrontendTriggerEnv): + """ + BUG: trigger idx 设置错误, 应该为 [0,1,6,8], 而不是 [0,1,2,3] + """ + await frontend_trigger_env.agent.reset() + + async def __smoke_task1(): + bp_update1 = BreakpointUpdateInfo() + bp_update1.chain = True + bp_update1.matchType = 2 + bp_update1.select = 0 + bp_update1.action = 0 + bp_update1.tdata2 = 0x1234_1234_5600 + + bp_update2 = BreakpointUpdateInfo() + bp_update2.chain = False + bp_update2.matchType = 0 + bp_update2.select = 0 + bp_update2.action = 0 + bp_update2.tdata2 = 0x1234_1234_5530 + + start = 0x1234_1234_5530 + pcs = [start + i * 2 for i in range(16)] + + bp_flags = BreakpointFlags() + bp_flags.debugMode = False + bp_flags.triggerCanRaiseBpExp = True + bp_flags.tEnableVec = [True, True, True, True] + + await frontend_trigger_env.agent.set_breakpoint_update( + 2, bp_update1, bp_flags=bp_flags + ) + await frontend_trigger_env.agent.set_breakpoint_update( + 3, bp_update2, bp_flags=bp_flags + ) + + await frontend_trigger_env.agent.bundle.step(2) + await frontend_trigger_env.agent.set_pcs(pcs) + await frontend_trigger_env.agent.bundle.step(2) + + async with Executor(exit="any") as exec: + exec(ticks_task(frontend_trigger_env.agent)) + exec(__smoke_task1()) + + # notify the refmodel to stop the simulation + await send_exit(frontend_trigger_env.agent) diff --git a/ut_frontend/ifu/frontend_trigger/test/test_chain.py b/ut_frontend/ifu/frontend_trigger/test/test_chain.py new file mode 100644 index 00000000..0278f1dd --- /dev/null +++ b/ut_frontend/ifu/frontend_trigger/test/test_chain.py @@ -0,0 +1,327 @@ +import toffee_test +from toffee import Executor + +from ut_frontend.ifu.frontend_trigger.agent.frontend_trigger_agent import ( + BreakpointFlags, + BreakpointUpdateInfo, +) +from ut_frontend.ifu.frontend_trigger.env.frontend_trigger_env import FrontendTriggerEnv +from ut_frontend.ifu.frontend_trigger.test.common_task import ( + ftrigger_chain_task1, + ticks_task, + ftrigger_common_task1, + send_exit, +) + +from ut_frontend.ifu.frontend_trigger.test.frontend_trigger_tools import ( + bp_flags_generator, + bp_update_generator, + get_mask_one, + int_generator, +) +from .frontend_trigger_fixture import frontend_trigger_env + + +@toffee_test.testcase +async def test_chain1_no_match( + frontend_trigger_env: FrontendTriggerEnv, +): + """ + 测试: chain 设为 True 时, 不应该有 trigger 触发 + """ + await frontend_trigger_env.agent.reset() + + # 创建生成器实例 + bp_update_gen = bp_update_generator( + pc_width=50, + condition=lambda x: x.select == 0 and x.chain == True, + # and x.action == 0, + ) + bp_flags_gen = bp_flags_generator( + condition=lambda x: x.debugMode == False + and all(x.tEnableVec) + and x.triggerCanRaiseBpExp + ) + pc_gen = int_generator( + min_value=0, max_value=get_mask_one(50) - 30, condition=lambda x: x & 1 == 0 + ) + + async with Executor(exit="any") as exec: + exec(ticks_task(frontend_trigger_env.agent)) + exec( + ftrigger_common_task1( + frontend_trigger_env.agent, bp_update_gen, bp_flags_gen, pc_gen + ) + ) + + # 通知参考模型停止模拟 + await send_exit(frontend_trigger_env.agent) + + +@toffee_test.testcase +async def test_chain_match_smoke(frontend_trigger_env: FrontendTriggerEnv): + """ + 测试: 正常情况下, chain 的触发 + """ + await frontend_trigger_env.agent.reset() + + # 创建生成器实例 + bp_update_chain_gen = bp_update_generator( + pc_width=50, + condition=lambda x: x.select == 0 and x.chain == True and x.matchType != 3, + # and x.action == 0, + ) + + bp_update_no_chain_gen = bp_update_generator( + pc_width=50, + condition=lambda x: x.select == 0 and x.chain == False and x.matchType != 3, + ) + + async def __smoke_task1(): + bp_update1 = BreakpointUpdateInfo() + bp_update1.chain = True + bp_update1.matchType = 2 + bp_update1.select = 0 + bp_update1.action = 0 + bp_update1.tdata2 = 0x1234_1234_5600 + + bp_update2 = BreakpointUpdateInfo() + bp_update2.chain = False + bp_update2.matchType = 0 + bp_update2.select = 0 + bp_update2.action = 0 + bp_update2.tdata2 = 0x1234_1234_5630 + + start = 0x1234_1234_5630 + pcs = [start + i * 2 for i in range(16)] + + bp_flags = BreakpointFlags() + bp_flags.debugMode = False + bp_flags.triggerCanRaiseBpExp = True + bp_flags.tEnableVec = [True, True, True, True] + + await frontend_trigger_env.agent.set_breakpoint_update( + 0, bp_update1, bp_flags=bp_flags + ) + await frontend_trigger_env.agent.set_breakpoint_update( + 1, bp_update2, bp_flags=bp_flags + ) + + await frontend_trigger_env.agent.bundle.step(2) + await frontend_trigger_env.agent.set_pcs(pcs) + await frontend_trigger_env.agent.bundle.step(2) + + async with Executor(exit="any") as exec: + exec(ticks_task(frontend_trigger_env.agent)) + exec(__smoke_task1()) + + # notify the refmodel to stop the simulation + await send_exit(frontend_trigger_env.agent) + + +@toffee_test.testcase +async def test_chain_no_match_smoke(frontend_trigger_env: FrontendTriggerEnv): + await frontend_trigger_env.agent.reset() + + async def __smoke_task1(): + bp_update1 = BreakpointUpdateInfo() + bp_update1.chain = True + bp_update1.matchType = 2 + bp_update1.select = 0 + bp_update1.action = 0 + bp_update1.tdata2 = 0x1234_1234_5600 + + bp_update2 = BreakpointUpdateInfo() + bp_update2.chain = False + bp_update2.matchType = 0 + bp_update2.select = 0 + bp_update2.action = 0 + bp_update2.tdata2 = 0x1234_1234_5530 + + start = 0x1234_1234_5530 + pcs = [start + i * 2 for i in range(16)] + + bp_flags = BreakpointFlags() + bp_flags.debugMode = False # TODO: 搞清楚这个参数的含义 + bp_flags.triggerCanRaiseBpExp = True + bp_flags.tEnableVec = [True, True, True, True] + + await frontend_trigger_env.agent.set_breakpoint_update( + 0, bp_update1, bp_flags=bp_flags + ) + await frontend_trigger_env.agent.set_breakpoint_update( + 1, bp_update2, bp_flags=bp_flags + ) + + await frontend_trigger_env.agent.bundle.step(2) + await frontend_trigger_env.agent.set_pcs(pcs) + await frontend_trigger_env.agent.bundle.step(2) + + async with Executor(exit="any") as exec: + exec(ticks_task(frontend_trigger_env.agent)) + exec(__smoke_task1()) + + # notify the refmodel to stop the simulation + await send_exit(frontend_trigger_env.agent) + + +@toffee_test.testcase +async def test_chain_match(frontend_trigger_env: FrontendTriggerEnv): + """ + 测试: 正常情况下, chain 的触发 + """ + await frontend_trigger_env.agent.reset() + + first_chain_bp_update_gen = bp_update_generator( + pc_width=50, condition=lambda x: x.select == 0 and x.chain == True + ) + second_chain_bp_update_gen = bp_update_generator( + pc_width=50, condition=lambda x: x.select == 0 and x.chain == False + ) + + bp_flags_gen = bp_flags_generator( + condition=lambda x: x.debugMode == False + and x.tEnableVec == [True, True, False, False] + and x.triggerCanRaiseBpExp + ) + pc_gen = int_generator( + min_value=0, max_value=get_mask_one(50) - 30, condition=lambda x: x & 1 == 0 + ) + + async with Executor(exit="any") as exec: + exec(ticks_task(frontend_trigger_env.agent)) + exec( + ftrigger_chain_task1( + agent=frontend_trigger_env.agent, + first_chain_bp_update_gen=first_chain_bp_update_gen, + second_chain_bp_update_gen=second_chain_bp_update_gen, + bp_flags_gen=bp_flags_gen, + pc_gen=pc_gen, + ) + ) + + # notify the refmodel to stop the simulation + await send_exit(frontend_trigger_env.agent) + + +@toffee_test.testcase +async def test_chain_no_match1(frontend_trigger_env: FrontendTriggerEnv): + """ + 测试: 任何一个链式断点不满足触发 select 条件时, 不应该触发 + """ + await frontend_trigger_env.agent.reset() + + first_chain_bp_update_gen = bp_update_generator( + pc_width=50, condition=lambda x: x.select == 1 and x.chain == True + ) + second_chain_bp_update_gen = bp_update_generator( + pc_width=50, condition=lambda x: x.select == 0 and x.chain == False + ) + + bp_flags_gen = bp_flags_generator( + condition=lambda x: x.debugMode == False + and x.tEnableVec == [True, True, False, False] + and x.triggerCanRaiseBpExp + ) + pc_gen = int_generator( + min_value=0, max_value=get_mask_one(50) - 30, condition=lambda x: x & 1 == 0 + ) + + async with Executor(exit="any") as exec: + exec(ticks_task(frontend_trigger_env.agent)) + exec( + ftrigger_chain_task1( + agent=frontend_trigger_env.agent, + first_chain_bp_update_gen=first_chain_bp_update_gen, + second_chain_bp_update_gen=second_chain_bp_update_gen, + bp_flags_gen=bp_flags_gen, + pc_gen=pc_gen, + ) + ) + + # notify the refmodel to stop the simulation + await send_exit(frontend_trigger_env.agent) + + +@toffee_test.testcase +async def test_chain_no_match2(frontend_trigger_env: FrontendTriggerEnv): + """ + 测试: 任何一个链式断点不满足触发 select 条件时, 不应该触发 + """ + await frontend_trigger_env.agent.reset() + + first_chain_bp_update_gen = bp_update_generator( + pc_width=50, condition=lambda x: x.select == 0 and x.chain == True + ) + second_chain_bp_update_gen = bp_update_generator( + pc_width=50, condition=lambda x: x.select == 1 and x.chain == False + ) + + bp_flags_gen = bp_flags_generator( + condition=lambda x: x.debugMode == False + and x.tEnableVec == [True, True, False, False] + and x.triggerCanRaiseBpExp + ) + pc_gen = int_generator( + min_value=0, max_value=get_mask_one(50) - 30, condition=lambda x: x & 1 == 0 + ) + + async with Executor(exit="any") as exec: + exec(ticks_task(frontend_trigger_env.agent)) + exec( + ftrigger_chain_task1( + agent=frontend_trigger_env.agent, + first_chain_bp_update_gen=first_chain_bp_update_gen, + second_chain_bp_update_gen=second_chain_bp_update_gen, + bp_flags_gen=bp_flags_gen, + pc_gen=pc_gen, + ) + ) + + # notify the refmodel to stop the simulation + await send_exit(frontend_trigger_env.agent) + + +@toffee_test.testcase +async def test_chain_no_match3(frontend_trigger_env: FrontendTriggerEnv): + """ + 测试: 任何一个链上的断点没有 enable 时, 链式断点不会触发 + """ + await frontend_trigger_env.agent.reset() + + first_chain_bp_update_gen = bp_update_generator( + pc_width=50, condition=lambda x: x.select == 0 and x.chain == True + ) + second_chain_bp_update_gen = bp_update_generator( + pc_width=50, condition=lambda x: x.select == 0 and x.chain == False + ) + + available_enable_vecs = [ + [True, False, False, False], + [False, True, False, False], + [False, False, False, False], + ] + bp_flags_gen = bp_flags_generator( + condition=lambda x: x.debugMode == False + and x.tEnableVec in available_enable_vecs + and x.triggerCanRaiseBpExp + ) + + pc_gen = int_generator( + min_value=0, max_value=get_mask_one(50) - 30, condition=lambda x: x & 1 == 0 + ) + + async with Executor(exit="any") as exec: + exec(ticks_task(frontend_trigger_env.agent)) + exec( + ftrigger_chain_task1( + agent=frontend_trigger_env.agent, + first_chain_bp_update_gen=first_chain_bp_update_gen, + second_chain_bp_update_gen=second_chain_bp_update_gen, + bp_flags_gen=bp_flags_gen, + pc_gen=pc_gen, + ) + ) + + # notify the refmodel to stop the simulation + await send_exit(frontend_trigger_env.agent) diff --git a/ut_frontend/ifu/frontend_trigger/test/test_normal_match.py b/ut_frontend/ifu/frontend_trigger/test/test_normal_match.py new file mode 100644 index 00000000..a68cc0a4 --- /dev/null +++ b/ut_frontend/ifu/frontend_trigger/test/test_normal_match.py @@ -0,0 +1,156 @@ +import pytest +import toffee_test +from toffee import Executor + +from comm.constants import TAG_LONG_TIME_RUN +from ut_frontend.ifu.frontend_trigger.env.frontend_trigger_env import FrontendTriggerEnv +from ut_frontend.ifu.frontend_trigger.test.common_task import ( + ticks_task, + ftrigger_common_task1, + send_exit, +) + +from ut_frontend.ifu.frontend_trigger.test.frontend_trigger_tools import ( + bp_flags_generator, + bp_update_generator, + get_mask_one, + int_generator, +) + +from .frontend_trigger_fixture import frontend_trigger_env + + +# N = 1000 +# T = 1 << 20 + + +# # @pytest.mark.toffee_tags(TAG_LONG_TIME_RUN) +# @pytest.mark.parametrize( +# "pc_min,pc_max", +# [(r * (T // N), (r + 1) * (T // N) if r < N else T) for r in range(N)], +# ) +# @toffee_test.testcase +# async def test_match_eq_new( +# frontend_trigger_env: FrontendTriggerEnv, pc_min: int, pc_max: int +# ): +# await frontend_trigger_env.agent.reset() + +# # 创建生成器实例 +# bp_update_gen = bp_update_generator( +# pc_width=50, +# condition=lambda x: x.matchType == 0 and x.select == 0 and x.chain == False, +# ) +# bp_flags_gen = bp_flags_generator( +# condition=lambda x: x.debugMode == False +# and all(x.tEnableVec) +# and x.triggerCanRaiseBpExp +# ) +# pc_gen = int_generator( +# min_value=pc_min, max_value=pc_max - 30, condition=lambda x: x & 1 == 0 +# ) + +# async with Executor(exit="any") as exec: +# exec(ticks_task(frontend_trigger_env.agent)) +# exec( +# ftrigger_common_task1( +# frontend_trigger_env.agent, bp_update_gen, bp_flags_gen, pc_gen +# ) +# ) + +# # 通知参考模型停止模拟 +# await send_exit(frontend_trigger_env.agent) + + +@toffee_test.testcase +async def test_match_eq_new(frontend_trigger_env: FrontendTriggerEnv): + await frontend_trigger_env.agent.reset() + + # 创建生成器实例 + bp_update_gen = bp_update_generator( + pc_width=50, + condition=lambda x: x.matchType == 0 and x.select == 0 and x.chain == False, + ) + bp_flags_gen = bp_flags_generator( + condition=lambda x: x.debugMode == False + and all(x.tEnableVec) + and x.triggerCanRaiseBpExp + ) + pc_gen = int_generator( + min_value=0, max_value=get_mask_one(50) - 30, condition=lambda x: x & 1 == 0 + ) + + async with Executor(exit="any") as exec: + exec(ticks_task(frontend_trigger_env.agent)) + exec( + ftrigger_common_task1( + frontend_trigger_env.agent, bp_update_gen, bp_flags_gen, pc_gen + ) + ) + + # 通知参考模型停止模拟 + await send_exit(frontend_trigger_env.agent) + + +@toffee_test.testcase +async def test_match_ge( + frontend_trigger_env: FrontendTriggerEnv, +): + await frontend_trigger_env.agent.reset() + + # 创建生成器实例 + bp_update_gen = bp_update_generator( + pc_width=50, + condition=lambda x: x.matchType == 2 and x.select == 0 and x.chain == False, + ) + bp_flags_gen = bp_flags_generator( + condition=lambda x: x.debugMode == False + and all(x.tEnableVec) + and x.triggerCanRaiseBpExp + ) + pc_gen = int_generator( + min_value=0, max_value=get_mask_one(50) - 30, condition=lambda x: x & 1 == 0 + ) + + async with Executor(exit="any") as exec: + exec(ticks_task(frontend_trigger_env.agent)) + exec( + ftrigger_common_task1( + frontend_trigger_env.agent, bp_update_gen, bp_flags_gen, pc_gen + ) + ) + + # 通知参考模型停止模拟 + await send_exit(frontend_trigger_env.agent) + + +@pytest.mark.toffee_tags(["BUG"]) +@toffee_test.testcase +async def test_match_lt( + frontend_trigger_env: FrontendTriggerEnv, +): + await frontend_trigger_env.agent.reset() + + # 创建生成器实例 + bp_update_gen = bp_update_generator( + pc_width=50, + condition=lambda x: x.matchType == 3 and x.select == 0 and x.chain == False, + ) + bp_flags_gen = bp_flags_generator( + condition=lambda x: x.debugMode == False + and all(x.tEnableVec) + and x.triggerCanRaiseBpExp + ) + pc_gen = int_generator( + min_value=0, max_value=get_mask_one(50) - 30, condition=lambda x: x & 1 == 0 + ) + + async with Executor(exit="any") as exec: + exec(ticks_task(frontend_trigger_env.agent)) + exec( + ftrigger_common_task1( + frontend_trigger_env.agent, bp_update_gen, bp_flags_gen, pc_gen + ) + ) + + # 通知参考模型停止模拟 + await send_exit(frontend_trigger_env.agent) diff --git a/ut_frontend/ifu/frontend_trigger/test/test_normal_no_match.py b/ut_frontend/ifu/frontend_trigger/test/test_normal_no_match.py new file mode 100644 index 00000000..8d643451 --- /dev/null +++ b/ut_frontend/ifu/frontend_trigger/test/test_normal_no_match.py @@ -0,0 +1,67 @@ +import pytest +import toffee_test +from toffee import Executor + +from ut_frontend.ifu.frontend_trigger.env.frontend_trigger_env import FrontendTriggerEnv +from ut_frontend.ifu.frontend_trigger.test.common_task import ( + ticks_task, + ftrigger_common_task1, + send_exit, +) + +from ut_frontend.ifu.frontend_trigger.test.frontend_trigger_tools import ( + bp_flags_generator, + bp_update_generator, + get_mask_one, + int_generator, +) +from .frontend_trigger_fixture import frontend_trigger_env, gr + + +@toffee_test.testcase +async def test_tselect1_no_match( + frontend_trigger_env: FrontendTriggerEnv, +): + + # 创建生成器实例 + bp_update_gen = bp_update_generator(pc_width=50, condition=lambda x: x.select == 1) + bp_flags_gen = bp_flags_generator(condition=lambda x: all(x.tEnableVec)) + pc_gen = int_generator( + min_value=0, max_value=get_mask_one(50) - 30, condition=lambda x: x & 1 == 0 + ) + + await frontend_trigger_env.agent.reset() + + async with Executor(exit="any") as exec: + exec(ticks_task(frontend_trigger_env.agent)) + exec( + ftrigger_common_task1( + frontend_trigger_env.agent, bp_update_gen, bp_flags_gen, pc_gen + ) + ) + + await send_exit(frontend_trigger_env.agent) + + +@toffee_test.testcase +async def test_enable0_no_match( + frontend_trigger_env: FrontendTriggerEnv, +): + # 创建生成器实例 + bp_update_gen = bp_update_generator(pc_width=50) + bp_flags_gen = bp_flags_generator(condition=lambda x: not any(x.tEnableVec)) + pc_gen = int_generator( + min_value=0, max_value=get_mask_one(50) - 30, condition=lambda x: x & 1 == 0 + ) + + await frontend_trigger_env.agent.reset() + + async with Executor(exit="any") as exec: + exec(ticks_task(frontend_trigger_env.agent)) + exec( + ftrigger_common_task1( + frontend_trigger_env.agent, bp_update_gen, bp_flags_gen, pc_gen + ) + ) + # notify the refmodel to stop the simulation + await send_exit(frontend_trigger_env.agent) diff --git a/uv.lock b/uv.lock new file mode 100644 index 00000000..fe2e6c0e --- /dev/null +++ b/uv.lock @@ -0,0 +1,406 @@ +version = 1 +revision = 1 +requires-python = ">=3.12" + +[[package]] +name = "allure-pytest" +version = "2.13.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "allure-python-commons" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2b/c3/829a300b72557e9327fa72692b62917dab3ad5bab678962ea84c066d4eef/allure-pytest-2.13.5.tar.gz", hash = "sha256:0ef8e1790c44a988db6b83c4d4f5e91451e2c4c8ea10601dfa88528d23afcf6e", size = 16976 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/7b/430830ed7bf1ef078f9e55eb4b19cc059ef5310a20b5bd73eeb99e2c5ff1/allure_pytest-2.13.5-py3-none-any.whl", hash = "sha256:94130bac32964b78058e62cf4b815ad97a5ac82a065e6dd2d43abac2be7640fc", size = 11606 }, +] + +[[package]] +name = "allure-python-commons" +version = "2.13.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "pluggy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2d/96/9991a10dcd25b98c8c3e4c7916780f33f13a55a0efbe8c7ea96505271d91/allure-python-commons-2.13.5.tar.gz", hash = "sha256:a232e7955811f988e49a4c1dd6c16cce7e9b81d0ea0422b1e5654d3254e2caf3", size = 12934 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/18/79a66d6adc301281803398f7288583dff8d1d3f2672c07ffab03ee12c7cd/allure_python_commons-2.13.5-py3-none-any.whl", hash = "sha256:8b0e837b6e32d810adec563f49e1d04127a5b6770e0232065b7cb09b9953980d", size = 15715 }, +] + +[[package]] +name = "ansi2html" +version = "1.9.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/d5/e3546dcd5e4a9566f4ed8708df5853e83ca627461a5b048a861c6f8e7a26/ansi2html-1.9.2.tar.gz", hash = "sha256:3453bf87535d37b827b05245faaa756dbab4ec3d69925e352b6319c3c955c0a5", size = 44300 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/71/aee71b836e9ee2741d5694b80d74bfc7c8cd5dbdf7a9f3035fcf80d792b1/ansi2html-1.9.2-py3-none-any.whl", hash = "sha256:dccb75aa95fb018e5d299be2b45f802952377abfdce0504c17a6ee6ef0a420c5", size = 17614 }, +] + +[[package]] +name = "attrs" +version = "25.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/7c/fdf464bcc51d23881d110abd74b512a42b3d5d376a55a831b44c603ae17f/attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e", size = 810562 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/30/d4986a882011f9df997a55e6becd864812ccfcd821d64aac8570ee39f719/attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a", size = 63152 }, +] + +[[package]] +name = "certifi" +version = "2024.12.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/bd/1d41ee578ce09523c81a15426705dd20969f5abf006d1afe8aeff0dd776a/certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db", size = 166010 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/32/8f6669fc4798494966bf446c8c4a162e0b5d893dff088afddf76414f70e1/certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", size = 164927 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 }, + { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 }, + { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 }, + { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 }, + { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 }, + { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 }, + { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 }, + { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 }, + { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 }, + { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 }, + { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 }, + { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 }, + { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 }, + { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 }, + { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 }, + { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 }, + { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 }, + { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 }, + { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 }, + { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 }, + { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 }, + { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 }, + { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 }, + { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 }, + { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 }, + { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 }, + { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "docutils" +version = "0.21.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 }, +] + +[[package]] +name = "execnet" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/ff/b4c0dc78fbe20c3e59c0c7334de0c27eb4001a2b2017999af398bf730817/execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3", size = 166524 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/09/2aea36ff60d16dd8879bdb2f5b3ee0ba8d08cbbdcdfe870e695ce3784385/execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc", size = 40612 }, +] + +[[package]] +name = "filelock" +version = "3.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/9c/0b15fb47b464e1b663b1acd1253a062aa5feecb07d4e597daea542ebd2b5/filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e", size = 18027 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/ec/00d68c4ddfedfe64159999e5f8a98fb8442729a63e2077eb9dcd89623d27/filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338", size = 16164 }, +] + +[[package]] +name = "htmlmin2" +version = "0.1.13" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/31/a76f4bfa885f93b8167cb4c85cf32b54d1f64384d0b897d45bc6d19b7b45/htmlmin2-0.1.13-py3-none-any.whl", hash = "sha256:75609f2a42e64f7ce57dbff28a39890363bde9e7e5885db633317efbdf8c79a2", size = 34486 }, +] + +[[package]] +name = "hypothesis" +version = "6.124.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "sortedcontainers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/ef/6e3736663ee67369f7f5b697674bfbd3efc91e7096ddd4452bbbc80065ff/hypothesis-6.124.7.tar.gz", hash = "sha256:8ed6c6ae47e7d26d869c1dc3dee04e8fc50c95240715bb9915ded88d6d920f0e", size = 416938 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/48/2412d4aacf1c50882126910ce036c92a838784915e3de66fb603a75c05ec/hypothesis-6.124.7-py3-none-any.whl", hash = "sha256:a6e1f66de84de3152d57f595a187a123ce3ecdea9dc8ef51ff8dcaa069137085", size = 479518 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, +] + +[[package]] +name = "jinja2" +version = "3.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/af/92/b3130cbbf5591acf9ade8708c365f3238046ac7cb8ccba6e81abccb0ccff/jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", size = 244674 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, +] + +[[package]] +name = "packaging" +version = "24.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, +] + +[[package]] +name = "pytest" +version = "8.3.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083 }, +] + +[[package]] +name = "pytest-asyncio" +version = "0.25.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/df/adcc0d60f1053d74717d21d58c0048479e9cab51464ce0d2965b086bd0e2/pytest_asyncio-0.25.2.tar.gz", hash = "sha256:3f8ef9a98f45948ea91a0ed3dc4268b5326c0e7bce73892acc654df4262ad45f", size = 53950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/d8/defa05ae50dcd6019a95527200d3b3980043df5aa445d40cb0ef9f7f98ab/pytest_asyncio-0.25.2-py3-none-any.whl", hash = "sha256:0d0bb693f7b99da304a0634afc0a4b19e49d5e0de2d670f38dc4bfa5727c5075", size = 19400 }, +] + +[[package]] +name = "pytest-reporter" +version = "0.5.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0a/1a/a5f46dd3b78dc207de5813342b3e51207d1bcae92207443536d0f6df24e2/pytest-reporter-0.5.3.tar.gz", hash = "sha256:426ae38f3c5304659635eff1d4080c8acf4dc6cd833469caed427a9a80bb41aa", size = 9815 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/76/2bac8243221186d8e8825912b95b04b3110c14f2cb4da6fbc476736e81e5/pytest_reporter-0.5.3-py3-none-any.whl", hash = "sha256:fa39fef46b1926b6f181c2759da0bcfd26b7697ef59d3c5cd260106c59b3a196", size = 7580 }, +] + +[[package]] +name = "pytest-reporter-html1" +version = "0.9.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ansi2html" }, + { name = "docutils" }, + { name = "htmlmin2" }, + { name = "jinja2" }, + { name = "pytest-reporter" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/c9/eda36ab2d7bd20acc9c8ce7a02ae188e3ac9f48971de84106d9f8df30ca6/pytest_reporter_html1-0.9.2.tar.gz", hash = "sha256:7aca7adb85ec2dd5bcf6b26cc46bda9887b970773f3af65752ee40b0b6d6cb14", size = 100184 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/26/d9d1661b425a8d7c84f693aeb53a8de5416798b452e62de271e123bfad6b/pytest_reporter_html1-0.9.2-py3-none-any.whl", hash = "sha256:e355e1af547b5345368d1dd025395751e5a8e9be0df5be7ae9403b692bf14053", size = 16514 }, +] + +[[package]] +name = "pytest-xdist" +version = "3.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "execnet" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/41/c4/3c310a19bc1f1e9ef50075582652673ef2bfc8cd62afef9585683821902f/pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d", size = 84060 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/82/1d96bf03ee4c0fdc3c0cbe61470070e659ca78dc0086fb88b66c185e2449/pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7", size = 46108 }, +] + +[[package]] +name = "pytoffee" +version = "0.2.3.dev20+g5d8fa90" +source = { git = "https://github.com/XS-MLVP/toffee?rev=master#5d8fa900c7a299b1be17449436c8e97ffe3e88a6" } + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[[package]] +name = "sortedcontainers" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575 }, +] + +[[package]] +name = "toffee-test" +version = "0.2.2.dev27+g4ca2605" +source = { git = "https://github.com/XS-MLVP/toffee-test?rev=master#4ca26053fda4b2822b774d4ea4ff0576ebbfeb99" } +dependencies = [ + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pytest-reporter-html1" }, + { name = "pytest-xdist" }, +] + +[[package]] +name = "unitychipforxiangshan" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "allure-pytest" }, + { name = "filelock" }, + { name = "hypothesis" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pytoffee" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "toffee-test" }, +] + +[package.metadata] +requires-dist = [ + { name = "allure-pytest", specifier = ">=2.13.5" }, + { name = "filelock", specifier = ">=3.9.0" }, + { name = "hypothesis", specifier = ">=6.124.7" }, + { name = "pytest", specifier = ">=8.3.4" }, + { name = "pytest-asyncio", specifier = ">=0.25.2" }, + { name = "pytoffee", git = "https://github.com/XS-MLVP/toffee?rev=master" }, + { name = "pyyaml", specifier = ">=6.0.1" }, + { name = "requests", specifier = ">=2.28.1" }, + { name = "toffee-test", git = "https://github.com/XS-MLVP/toffee-test?rev=master" }, +] + +[[package]] +name = "urllib3" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 }, +] From 6c0cfded42bcc16bf6cdca6fbc9fcf236d9015bb Mon Sep 17 00:00:00 2001 From: leesum <1255273338@qq.com> Date: Sat, 15 Feb 2025 18:05:10 +0800 Subject: [PATCH 2/2] Add BUG description --- .gitignore | 5 ++ .../agent/frontend_trigger_agent.py | 8 ++- .../ifu/frontend_trigger/test/common_task.py | 15 +---- .../ifu/frontend_trigger/test/demo_test.py | 8 +-- .../test/frontend_trigger_fixture.py | 2 +- .../test/test_bug_examples.py | 58 ++++++++++++++++++- .../test/test_normal_match.py | 41 +------------ 7 files changed, 70 insertions(+), 67 deletions(-) diff --git a/.gitignore b/.gitignore index 30d1a07f..37bf4a37 100644 --- a/.gitignore +++ b/.gitignore @@ -171,3 +171,8 @@ cython_debug/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ + +# vscode +.vscode/ +*.fst +*.dat \ No newline at end of file diff --git a/ut_frontend/ifu/frontend_trigger/agent/frontend_trigger_agent.py b/ut_frontend/ifu/frontend_trigger/agent/frontend_trigger_agent.py index 41e3df0a..159980c2 100644 --- a/ut_frontend/ifu/frontend_trigger/agent/frontend_trigger_agent.py +++ b/ut_frontend/ifu/frontend_trigger/agent/frontend_trigger_agent.py @@ -239,5 +239,9 @@ def get_old_pcs(self) -> list[int]: async def send_cycle(self, new_cycle: int): self.cycle = new_cycle await self.bundle.step() - # BUG: driver_method 有返回值时,不应该用于 ref model 的比对 - return self.cycle + 1 + # BUG: driver_method 有返回值时,不应该用于 ref model 的比对,日志如下 + # TOFFEE_WARNING @_base_agent.py:131: The dut result is 8, but the model result is None. + # TOFFEE_WARNING @_base_agent.py:131: The dut result is 9, but the model result is None. + # TOFFEE_WARNING @_base_agent.py:131: The dut result is 10, but the model result is None. + # 取消下面一行注释,即可复现 + # return self.cycle + 1 diff --git a/ut_frontend/ifu/frontend_trigger/test/common_task.py b/ut_frontend/ifu/frontend_trigger/test/common_task.py index 971ab10d..2f7df25f 100644 --- a/ut_frontend/ifu/frontend_trigger/test/common_task.py +++ b/ut_frontend/ifu/frontend_trigger/test/common_task.py @@ -108,20 +108,7 @@ async def ftrigger_chain_task1( bound_pair_list = list(set(islice(bound_pair_gen, 20))) for bound1, bound2 in bound_pair_list: - # first_chain_bp = BreakpointUpdateInfo( - # matchType=match_type_map[bound1[0]], - # select=0, - # action=random.randint(0, 1), - # chain=True, - # tdata2=to_even(bound1[1]), - # ) - # second_chain_bp = BreakpointUpdateInfo( - # matchType=match_type_map[bound2[0]], - # select=0, - # action=random.randint(0, 1), - # chain=False, - # tdata2=to_even(bound2[1]), - # ) + first_chain_bp = next(first_chain_bp_update_gen) second_chain_bp = next(second_chain_bp_update_gen) diff --git a/ut_frontend/ifu/frontend_trigger/test/demo_test.py b/ut_frontend/ifu/frontend_trigger/test/demo_test.py index 8e7f911b..9c9cab67 100644 --- a/ut_frontend/ifu/frontend_trigger/test/demo_test.py +++ b/ut_frontend/ifu/frontend_trigger/test/demo_test.py @@ -30,7 +30,7 @@ async def test_smoke(frontend_trigger_env: FrontendTriggerEnv): async def __smoke_task1(): bp_update = BreakpointUpdateInfo() bp_update.chain = False - bp_update.matchType = 3 + bp_update.matchType = 0 bp_update.select = 0 bp_update.action = 0 bp_update.tdata2 = 0x1234_1234_5678 @@ -50,12 +50,6 @@ async def __smoke_task1(): await frontend_trigger_env.agent.bundle.step(2) await frontend_trigger_env.agent.set_pcs(pcs) await frontend_trigger_env.agent.bundle.step(2) - # await frontend_trigger_env.agent.set_pcs(list(map(lambda x: x + 2, pcs))) - # await frontend_trigger_env.agent.bundle.step(2) - # await frontend_trigger_env.agent.set_pcs(list(map(lambda x: x - 2, pcs))) - # bp_flags.tEnableVec = [True, True, True, True] - # await frontend_trigger_env.agent.set_breakpoint_flags(bp_flags) - # await frontend_trigger_env.agent.bundle.step(2) async with Executor(exit="any") as exec: exec(ticks_task(frontend_trigger_env.agent)) diff --git a/ut_frontend/ifu/frontend_trigger/test/frontend_trigger_fixture.py b/ut_frontend/ifu/frontend_trigger/test/frontend_trigger_fixture.py index f73daae7..0d80c61d 100644 --- a/ut_frontend/ifu/frontend_trigger/test/frontend_trigger_fixture.py +++ b/ut_frontend/ifu/frontend_trigger/test/frontend_trigger_fixture.py @@ -29,7 +29,7 @@ def init_frontend_trigger_funcov(dut: DUTFrontendTrigger, g: fc.CovGroup): ) # Reverse mark function coverage to the check point - # TODO: 检查为什么无效 + # BUG: 加不加 mark_function 对最后的 funcov 都没有影响 for i in range(16): g.mark_function( f"PC{i}_TRIGGERED", diff --git a/ut_frontend/ifu/frontend_trigger/test/test_bug_examples.py b/ut_frontend/ifu/frontend_trigger/test/test_bug_examples.py index f090fa3c..a2442d7c 100644 --- a/ut_frontend/ifu/frontend_trigger/test/test_bug_examples.py +++ b/ut_frontend/ifu/frontend_trigger/test/test_bug_examples.py @@ -2,15 +2,22 @@ import toffee_test from toffee import Executor +from comm.constants import TAG_LONG_TIME_RUN from ut_frontend.ifu.frontend_trigger.agent.frontend_trigger_agent import ( BreakpointFlags, BreakpointUpdateInfo, ) from ut_frontend.ifu.frontend_trigger.env.frontend_trigger_env import FrontendTriggerEnv from ut_frontend.ifu.frontend_trigger.test.common_task import ( + ftrigger_common_task1, ticks_task, send_exit, ) +from ut_frontend.ifu.frontend_trigger.test.frontend_trigger_tools import ( + bp_flags_generator, + bp_update_generator, + int_generator, +) from .frontend_trigger_fixture import frontend_trigger_env @@ -20,7 +27,8 @@ @toffee_test.testcase async def test_bug_match_lt(frontend_trigger_env: FrontendTriggerEnv): """ - 当 matchType 为 3 时(小于), 断点比较逻辑出错 + BUG:当 matchType 为 3 时(小于), 断点比较逻辑出错 + BUG:在 refmodel 中, 即使对比失败,最后还是会输出 passed(测试环境无法捕获 assert) """ await frontend_trigger_env.agent.reset() @@ -30,7 +38,7 @@ async def __smoke_task1(): bp_update.matchType = 3 bp_update.select = 0 bp_update.action = 0 - bp_update.tdata2 = 0xDEAD_DEAD_BEE5 + bp_update.tdata2 = 0xDEAD_DEAD_BEE6 start = 0xDEAD_DEAD_BEE4 pcs = [start + i * 2 for i in range(16)] @@ -47,13 +55,13 @@ async def __smoke_task1(): await frontend_trigger_env.agent.bundle.step(2) await frontend_trigger_env.agent.set_pcs(pcs) await frontend_trigger_env.agent.bundle.step(2) - async with Executor(exit="any") as exec: exec(ticks_task(frontend_trigger_env.agent)) exec(__smoke_task1()) # notify the refmodel to stop the simulation + # BUG:必须要手动通知 refmodel 停止, refmodel 不会自动停止 await send_exit(frontend_trigger_env.agent) @@ -105,3 +113,47 @@ async def __smoke_task1(): # notify the refmodel to stop the simulation await send_exit(frontend_trigger_env.agent) + + +N = 1000 +T = 1 << 50 + + +@pytest.mark.toffee_tags(["BUG", TAG_LONG_TIME_RUN]) +@pytest.mark.parametrize( + "pc_min,pc_max", + [(r * (T // N), (r + 1) * (T // N) if r < N else T) for r in range(N)], +) +@toffee_test.testcase +async def test_match_eq_new( + frontend_trigger_env: FrontendTriggerEnv, pc_min: int, pc_max: int +): + """ + BUG: 启用功能覆盖率,如果测试数量过多,bash 中的 ARGMAX 过长导致程序崩溃 + """ + await frontend_trigger_env.agent.reset() + + # 创建生成器实例 + bp_update_gen = bp_update_generator( + pc_width=50, + condition=lambda x: x.matchType == 0 and x.select == 0 and x.chain == False, + ) + bp_flags_gen = bp_flags_generator( + condition=lambda x: x.debugMode == False + and all(x.tEnableVec) + and x.triggerCanRaiseBpExp + ) + pc_gen = int_generator( + min_value=pc_min, max_value=pc_max - 30, condition=lambda x: x & 1 == 0 + ) + + async with Executor(exit="any") as exec: + exec(ticks_task(frontend_trigger_env.agent)) + exec( + ftrigger_common_task1( + frontend_trigger_env.agent, bp_update_gen, bp_flags_gen, pc_gen + ) + ) + + # 通知参考模型停止模拟 + await send_exit(frontend_trigger_env.agent) diff --git a/ut_frontend/ifu/frontend_trigger/test/test_normal_match.py b/ut_frontend/ifu/frontend_trigger/test/test_normal_match.py index a68cc0a4..c7823f40 100644 --- a/ut_frontend/ifu/frontend_trigger/test/test_normal_match.py +++ b/ut_frontend/ifu/frontend_trigger/test/test_normal_match.py @@ -20,49 +20,10 @@ from .frontend_trigger_fixture import frontend_trigger_env -# N = 1000 -# T = 1 << 20 - - -# # @pytest.mark.toffee_tags(TAG_LONG_TIME_RUN) -# @pytest.mark.parametrize( -# "pc_min,pc_max", -# [(r * (T // N), (r + 1) * (T // N) if r < N else T) for r in range(N)], -# ) -# @toffee_test.testcase -# async def test_match_eq_new( -# frontend_trigger_env: FrontendTriggerEnv, pc_min: int, pc_max: int -# ): -# await frontend_trigger_env.agent.reset() - -# # 创建生成器实例 -# bp_update_gen = bp_update_generator( -# pc_width=50, -# condition=lambda x: x.matchType == 0 and x.select == 0 and x.chain == False, -# ) -# bp_flags_gen = bp_flags_generator( -# condition=lambda x: x.debugMode == False -# and all(x.tEnableVec) -# and x.triggerCanRaiseBpExp -# ) -# pc_gen = int_generator( -# min_value=pc_min, max_value=pc_max - 30, condition=lambda x: x & 1 == 0 -# ) - -# async with Executor(exit="any") as exec: -# exec(ticks_task(frontend_trigger_env.agent)) -# exec( -# ftrigger_common_task1( -# frontend_trigger_env.agent, bp_update_gen, bp_flags_gen, pc_gen -# ) -# ) - -# # 通知参考模型停止模拟 -# await send_exit(frontend_trigger_env.agent) @toffee_test.testcase -async def test_match_eq_new(frontend_trigger_env: FrontendTriggerEnv): +async def test_match_eq(frontend_trigger_env: FrontendTriggerEnv): await frontend_trigger_env.agent.reset() # 创建生成器实例