Weiqiang Wang

148 papers A* 8A 13B 27C 4Misc 7Journal 60Unranked 29
YearRankTypeTitle / Venue / Authors
2025 J jnl
Neural Comput. Appl.
Xiaobin Li, Weiqiang Wang, Guangluan Xu
2025 J jnl
CoRR
Meiqi Wu, Yaxuan Kang, Xuchen Li, Shiyu Hu, Xiaotang Chen, Yunfeng Kang, Weiqiang Wang, Kaiqi Huang
2024 J jnl
CoRR
Lianlei Shan, Weiqiang Wang, Ke Lv, Bin Luo
2024 J jnl
CoRR
Meiqi Wu, Kaiqi Huang, Yuanqiang Cai, Shiyu Hu, Yuzhong Zhao, Weiqiang Wang
2024 J jnl
IEEE Trans. Circuits Syst. Video Technol.
Meiqi Wu, Kaiqi Huang, Yuanqiang Cai, Shiyu Hu, Yuzhong Zhao, Weiqiang Wang
2024 Misc conf
ICASSP
Xingyu Ding, Weiqiang Wang
2024 conf
PRCV (9)
Meiqi Wu, Yaxuan Kang, Xuchen Li, Shiyu Hu, Xiaotang Chen, Yunfeng Kang, Weiqiang Wang, Kaiqi Huang
2023 J jnl
IEEE Trans. Geosci. Remote. Sens.
Lianlei Shan, Weiqiang Wang, Ke Lv, Bin Luo
2023 B conf
ICIP
Guiqin Zhao, Weiqiang Wang
2023 J jnl
Pattern Recognit.
Ji Gan, Yuyan Chen, Bo Hu, Jiaxu Leng, Weiqiang Wang, Xinbo Gao
2023 J jnl
IEEE Trans. Circuits Syst. Video Technol.
Peirui Cheng, Yuzhong Zhao, Weiqiang Wang
2023 A conf
ICME
Yuzhong Zhao, Yuanqiang Cai, Weijia Wu, Weiqiang Wang
2023 A conf
ICME
Yuzhong Zhao, Weijia Wu, Zhuang Li, Jiahong Li, Weiqiang Wang
2023 J jnl
CoRR
Yuzhong Zhao, Weijia Wu, Zhuang Li, Jiahong Li, Weiqiang Wang
2023 J jnl
IEEE Trans. Neural Networks Learn. Syst.
Xiaobin Li, Weiqiang Wang
2023 J jnl
ACM Trans. Graph.
Ji Gan, Weiqiang Wang, Jiaxu Leng, Xinbo Gao
2022 J jnl
IEEE Trans. Geosci. Remote. Sens.
Lianlei Shan, Weiqiang Wang, Ke Lv, Bin Luo
2022 J jnl
IEEE Trans. Geosci. Remote. Sens.
Lianlei Shan, Weiqiang Wang, Ke Lv, Bin Luo
2022 J jnl
IEEE Geosci. Remote. Sens. Lett.
Si-Bao Chen, Yu-Xin Ji, Jin Tang, Bin Luo, Weiqiang Wang, Ke Lv
2022 J jnl
IEEE Geosci. Remote. Sens. Lett.
Lianlei Shan, Weiqiang Wang
2022 J jnl
Neurocomputing
Peirui Cheng, Yuzhong Zhao, Yuanqiang Cai, Weiqiang Wang
2022 J jnl
CoRR
Yuzhong Zhao, Yuanqiang Cai, Weijia Wu, Weiqiang Wang
2022 A* conf
CVPR
Weixi Zhao, Weiqiang Wang, Yunjie Tian
2022 Misc conf
ICASSP
Lianlei Shan, Weiqiang Wang
2021 J jnl
IEEE J. Sel. Top. Appl. Earth Obs. Remote. Sens.
Si-Bao Chen, Bei-Min Dai, Jin Tang, Bin Luo, Weiqiang Wang, Ke Lv
2021 Misc conf
ICASSP
Lianlei Shan, Xiaobin Li, Weiqiang Wang
2021 Misc conf
ICASSP
Xiaobin Li, Lianlei Shan, Weiqiang Wang
2021 J jnl
CoRR
Weixi Zhao, Yunjie Tian, Qixiang Ye, Jianbin Jiao, Weiqiang Wang
2021 A* conf
AAAI
Ji Gan, Weiqiang Wang
2021 A* conf
AAAI
Yuanqiang Cai, Longyin Wen, Libo Zhang, Dawei Du, Weiqiang Wang
2021 J jnl
IEEE Trans. Circuits Syst. Video Technol.
Yuanqiang Cai, Chang Liu, Peirui Cheng, Dawei Du, Libo Zhang, Weiqiang Wang, Qixiang Ye
2020 J jnl
IEEE Trans. Circuits Syst. Video Technol.
Peirui Cheng, Yuanqiang Cai, Weiqiang Wang
2020 J jnl
CoRR
Ji Gan, Weiqiang Wang, Ke Lu
2020 J jnl
Pattern Recognit. Lett.
Ji Gan, Weiqiang Wang, Ke Lu
2020 B conf
ICPR
Xiaobin Li, Lianlei Shan, Minglong Li, Weiqiang Wang
2020 B conf
ICPR
Minglong Li, Lianlei Shan, Xiaobin Li, Yang Bai, Dengji Zhou, Weiqiang Wang, Ke Lv, Bin Luo, Si-Bao Chen
2020 A* conf
ACM Multimedia
Yuanqiang Cai, Dawei Du, Libo Zhang, Longyin Wen, Weiqiang Wang, Yanjun Wu, Siwei Lyu
2020 J jnl
Pattern Recognit.
Yuanqiang Cai, Weiqiang Wang, Yuting Chen, Qixiang Ye
2020 J jnl
Pattern Recognit.
Ji Gan, Weiqiang Wang, Ke Lu
2020 J jnl
Pattern Recognit.
Xiaobin Li, Weiqiang Wang
2020 J jnl
Pattern Recognit.
Hongzhu Li, Weiqiang Wang
2020 J jnl
Neural Comput. Appl.
Yuanqiang Cai, Weiqiang Wang
2020 J jnl
Neural Comput. Appl.
Yuanqiang Cai, Weiqiang Wang, Haiqing Ren, Ke Lu
2020 J jnl
CoRR
Yuanqiang Cai, Chang Liu, Weiqiang Wang, Qixiang Ye
2020 B conf
ICPR
Lianlei Shan, Minglong Li, Xiaobin Li, Yang Bai, Ke Lv, Bin Luo, Si-Bao Chen, Weiqiang Wang
2019 J jnl
CoRR
Hongzhu Li, Weiqiang Wang
2019 J jnl
Pattern Recognit. Lett.
Haiqing Ren, Weiqiang Wang, Xiwen Qu, Yuanqiang Cai
2019 J jnl
Inf. Sci.
Ji Gan, Weiqiang Wang, Ke Lu
2019 A conf
ICME
Yang Bai, Weiqiang Wang
2019 J jnl
CoRR
Yuanqiang Cai, Dawei Du, Libo Zhang, Longyin Wen, Weiqiang Wang, Yanjun Wu, Siwei Lyu
2019 J jnl
Neural Comput. Appl.
Ji Gan, Weiqiang Wang
2019 J jnl
CoRR
Xiaobin Li, Weiqiang Wang
2019 A conf
ICME
Peirui Cheng, Weiqiang Wang, Yuanqiang Cai
2019 J jnl
Multim. Tools Appl.
Hongzhu Li, Weiqiang Wang, Ke Lv
2019 J jnl
Pattern Recognit.
Haiqing Ren, Weiqiang Wang, Chenglin Liu
2018 B conf
ICMR
Peirui Cheng, Weiqiang Wang
2018 A conf
ICME
Ji Gan, Weiqiang Wang, Ke Lu
2018 J jnl
J. Vis. Commun. Image Represent.
Zhangjian Ji, Weiqiang Wang
2018 J jnl
Pattern Recognit. Lett.
Xiwen Qu, Weiqiang Wang, Ke Lu, Jianshe Zhou
2018 J jnl
Frontiers Inf. Technol. Electron. Eng.
Chaochao Bai, Weiqiang Wang, Tong Zhao, Ruxin Wang, Mingqiang Li
2018 J jnl
ACM Trans. Multim. Comput. Commun. Appl.
Shao Huang, Weiqiang Wang, Shengfeng He, Rynson W. H. Lau
2018 J jnl
IEEE Trans. Image Process.
Shao Huang, Weiqiang Wang, Shengfeng He, Rynson W. H. Lau
2018 J jnl
IEICE Trans. Inf. Syst.
Jinhua Wang, Weiqiang Wang, Guangmei Xu, Hongzhe Liu
2018 A conf
ICME
Kang Shi, Weiqiang Wang, Changsheng Xu
2018 J jnl
Neurocomputing
Chaochao Bai, Weiqiang Wang, Tong Zhao, Mingqiang Li
2018 J jnl
Pattern Recognit.
Xiwen Qu, Weiqiang Wang, Ke Lu, Jianshe Zhou
2018 J jnl
IEEE Access
Chaochao Bai, Mingqiang Li, Tong Zhao, Weiqiang Wang
2018 B conf
ICPR
Usman Muhammad, Weiqiang Wang, Shahbaz Pervez Chattha, Sajid Ali
2018 J jnl
Multim. Tools Appl.
Yuanqiang Cai, Weiqiang Wang, Shao Huang, Jin Ma, Ke Lu
2017 conf
PCM (1)
Jianshe Zhou, Zhaochun Xu, Jie Liu, Weiqiang Wang, Ke Lu
2017 A conf
ICME
Haiqing Ren, Weiqiang Wang, Ke Lu, Jianshe Zhou, Qiuchen Yuan
2017 J jnl
CoRR
Shao Huang, Weiqiang Wang, Shengfeng He, Rynson W. H. Lau
2017 J jnl
Multim. Tools Appl.
Jinhua Wang, Weiqiang Wang, Bing Li, Guangmei Xu, Ruizhe Zhang, Jingzun Zhang
2017 conf
PCM (1)
Hongzhu Li, Weiqiang Wang
2017 J jnl
Pattern Recognit. Lett.
Shao Huang, Weiqiang Wang
2017 A conf
ICME
Jin Ma, Weiqiang Wang, Ke Lu, Jianshe Zhou
2017 J jnl
IEEE Trans. Image Process.
Shao Huang, Weiqiang Wang, Shengfeng He, Rynson W. H. Lau
2017 conf
PCM (1)
Kang Shi, Weiqiang Wang
2016 J jnl
IEEE Trans. Image Process.
Ping Hu, Weiqiang Wang, Chi Zhang, Ke Lu
2016 B conf
ICPR
Shao Huang, Weiqiang Wang, Ke Lu
2016 A conf
ICME
Xiwen Qu, Weiqiang Wang, Ke Lu, Zhangjian Ji
2016 B conf
ICPR
Xiwen Qu, Weiqiang Wang, Ke Lu
2016 conf
ICB
Chaochao Bai, Weiqiang Wang, Tong Zhao, Mingqiang Li
2016 B conf
ICIP
Zhaochun Xu, Weiqiang Wang, Ke Lu
2015 conf
ICIMCS
Ning Xu, Weiqiang Wang, Xiwen Qu
2015 B conf
ICIP
Ping Hu, Weiqiang Wang, Ke Lu
2015 conf
ICIC (1)
Chaochao Bai, Tong Zhao, Weiqiang Wang, Min Wu
2015 J jnl
Multim. Tools Appl.
Xiaoqian Liu, Weiqiang Wang
2015 J jnl
Multim. Tools Appl.
Zhong Li, Weiqiang Wang, Xiaoqian Liu, Ke Lu
2015 A* conf
ACM Multimedia
Ping Hu, Weiqiang Wang, Ke Lu
2015 B conf
ICIP
Xiwen Qu, Weiqiang Wang, Ke Lu, Ning Xu
2015 J jnl
J. Vis. Commun. Image Represent.
Zhangjian Ji, Weiqiang Wang
2015 conf
PCM (1)
Ning Xu, Weiqiang Wang, Xiwen Qu
2015 conf
ICIG (3)
Ning Xu, Weiqiang Wang, Xiwen Qu
2015 B conf
ICIP
Shao Huang, Weiqiang Wang
2015 B conf
ICIP
Zhangjian Ji, Weiqiang Wang, Ke Lu
2015 Misc conf
MVA
Xiwen Qu, Ning Xu, Weiqiang Wang, Ke Lu
2015 conf
ACPR
Ping Hu, Weiqiang Wang, Ke Lu
2014 B conf
ICPR
Shao Huang, Weiqiang Wang
2014 J jnl
Pattern Recognit.
Zhangjian Ji, Weiqiang Wang
2014 B conf
ICIP
Shao Huang, Weiqiang Wang, Hui Zhang
2014 A conf
ICME
Zhangjian Ji, Weiqiang Wang, Ning Xu
2014 B conf
ICIP
Zhangjian Ji, Weiqiang Wang
2014 B conf
ICPR
Zhike Zhang, Weiqiang Wang, Ke Lu
2013 B conf
SMC
Zhike Zhang, Weiqiang Wang
2013 B conf
ICIP
Zhangjian Ji, Weiqiang Wang, Ke Lu
2013 B conf
SMC
Zhangjian Ji, Weiqiang Wang, Ke Lv
2013 conf
PCM
Xiaoqian Liu, Weiqiang Wang
2013 B conf
ICIP
Chi Zhang, Weiqiang Wang
2012 A* conf
ACM Multimedia
Chi Zhang, Weiqiang Wang
2012 B conf
ICPR
Xiaoqian Liu, Ke Lu, Weiqiang Wang
2012 J jnl
IEEE Trans. Multim.
Xiaoqian Liu, Weiqiang Wang
2010 conf
ICNSC
Shugao Ma, Weiqiang Wang
2010 B conf
ICPR
Xiaoqian Liu, Weiqiang Wang, Tingshao Zhu
2010 A* conf
ACM Multimedia
Xiaoqian Liu, Weiqiang Wang
2009 A conf
ICME
Xiaojun Li, Weiqiang Wang, Qingming Huang, Wen Gao, Laiyun Qing
2009 conf
PCM
Jian Lin, Weiqiang Wang
2008 conf
PCM
Yu Gong, Weiqiang Wang, Shuqiang Jiang, Qingming Huang, Wen Gao
2008 B conf
ICPR
Shugao Ma, Weiqiang Wang, Qingming Huang, Shuqiang Jiang, Wen Gao
2008 B conf
ICIP
Xiaojun Li, Weiqiang Wang, Shuqiang Jiang, Qingming Huang, Wen Gao
2008 B conf
ICPR
Dong Zhang, Weiqiang Wang, Qingming Huang, Shuqiang Jiang, Wen Gao
2008 J jnl
IEEE Trans. Circuits Syst. Video Technol.
Weiqiang Wang, Jie Yang, Wen Gao
2007 A conf
ICME
Qianhui Ning, Weiqiang Wang, Caifeng Zhu, Laiyun Qing, Qingming Huang
2007 A conf
ICME
Dong Zhang, Weiqiang Wang, Wen Gao, Shuqiang Jiang
2007 conf
ICIP (5)
Yuanning Li, Weiqiang Wang, Wen Gao
2007 conf
PCM
Weiqiang Wang, Libo Fu, Wen Gao
2006 conf
PCM
Jia Li, Ting Liu, Weiqiang Wang, Wen Gao
2006 conf
PCM
Yuanning Li, Weiqiang Wang, Wen Gao
2006 conf
ICPR (2)
Yaowen Zhan, Weiqiang Wang, Wen Gao
2006 A* conf
ACM Multimedia
Qing-Fang Zheng, Weiqiang Wang, Wen Gao
2006 J jnl
Int. J. Image Graph.
Qing-Fang Zheng, Wei Zeng, Weiqiang Wang, Wen Gao
2006 conf
PCM
Caifeng Zhu, Weiqiang Wang, Qianhui Ning
2006 conf
ICPR (2)
Lei Qin, Weiqiang Wang, Qingming Huang, Wen Gao
2005 conf
PCM (1)
Libo Fu, Weiqiang Wang, Yaowen Zhan
2005 conf
ICASSP (2)
Lei Qin, Wei Zeng, Wen Gao, Weiqiang Wang
2005 C conf
ISM
Yushi Wang, Weiqiang Wang, Wen Gao
2004 conf
PCM (1)
Yaowei Wang, Weiqiang Wang, Yanfei Wang
2004 B conf
ICIP
Yang Liu, Weiqiang Wang, Wen Gao, Wei Zeng
2004 conf
PCM (3)
Lei Qin, Wei Zeng, Weiqiang Wang
2004 Misc conf
ICIG
Qing-Fang Zheng, Wei Zeng, Wen Gao, Weiqiang Wang
2004 Misc conf
ICIG
Ming-Ji Zhang, Weiqiang Wang, Qing-Fang Zheng, Wen Gao
2003 C conf
IDEAL
Laiyun Qing, Weiqiang Wang, Wen Gao
2003 C conf
IDEAL
Qixiang Ye, Wen Gao, Wei Zeng, Tao Zhang, Weiqiang Wang, Yang Liu
2002 conf
IEEE Pacific Rim Conference on Multimedia
Laiyun Qing, Weiqiang Wang, Tiejun Huang, Wen Gao
2002 conf
IEEE Pacific Rim Conference on Multimedia
Weiqiang Wang, Wen Gao
2001 conf
IEEE Pacific Rim Conference on Multimedia
Weiqiang Wang, Wen Gao
2001 conf
IEEE Pacific Rim Conference on Multimedia
Weiqiang Wang, Wen Gao
2000 C conf
IDEAL
Weiqiang Wang, Wen Gao, Jintao Li, Shouxun Lin
tests/unit/test_decompile_analysis.py
← Index tests/unit/test_decompile_analysis.py python
"""Unit tests (mocked Binary Ninja) for analysis modules:
- bninja/analysis/cfg.py — CFGAnalysis
- bninja/analysis/disassembly.py — DisassemblyAnalysis
- bninja/analysis/low_level.py — LowLevelAnalysis
"""
import sys
import pytest
from unittest.mock import MagicMock

from tests.unit.conftest_binja_stubs import (
    install_binja_stubs,
    BranchType,
    InstructionTextTokenType,
    MockBasicBlock,
    MockEdge,
    MockFunction,
    MockToken,
    MockDisassemblyLine,
    MockBinaryView,
    MockSymbol,
    SymbolType,
    LowLevelILOperation,
)

install_binja_stubs()

from redb.extractors.decompiler.bninja.analysis.cfg import CFGAnalysis
from redb.extractors.decompiler.bninja.analysis.disassembly import DisassemblyAnalysis
from redb.extractors.decompiler.bninja.arch.x86 import Arch_x86


# ============================================================================
# 9a. CFGAnalysis
# ============================================================================


class TestCFGCyclomaticComplexity:
    def test_cyclomatic_complexity_linear(self):
        """Single block, no edges: E - N + 2 = 0 - 1 + 2 = 1."""
        block = MockBasicBlock(start=0x1000, end=0x1010, outgoing_edges=[])
        func = MockFunction(start=0x1000, basic_blocks=[block])
        cfg = CFGAnalysis(func)
        result = cfg.extract_function_cfg()
        assert result["cyclomatic_complexity"] == 1

    def test_cyclomatic_complexity_branch(self):
        """Diamond: 4 blocks, 4 edges -> 4 - 4 + 2 = 2."""
        entry = MockBasicBlock(start=0x1000, end=0x1010)
        true_b = MockBasicBlock(start=0x1010, end=0x1020)
        false_b = MockBasicBlock(start=0x1020, end=0x1030)
        merge = MockBasicBlock(start=0x1030, end=0x1040)

        entry.outgoing_edges = [MockEdge(target=true_b), MockEdge(target=false_b)]
        true_b.outgoing_edges = [MockEdge(target=merge)]
        false_b.outgoing_edges = [MockEdge(target=merge)]
        merge.outgoing_edges = []

        func = MockFunction(start=0x1000, basic_blocks=[entry, true_b, false_b, merge])
        cfg = CFGAnalysis(func)
        result = cfg.extract_function_cfg()
        assert result["cyclomatic_complexity"] == 2

    def test_cyclomatic_complexity_loop(self):
        """Loop: 3 blocks, 3 edges -> 3 - 3 + 2 = 2."""
        header = MockBasicBlock(start=0x1000, end=0x1010)
        body = MockBasicBlock(start=0x1010, end=0x1020)
        exit_b = MockBasicBlock(start=0x1020, end=0x1030)

        header.outgoing_edges = [MockEdge(target=body), MockEdge(target=exit_b)]
        body.outgoing_edges = [MockEdge(target=header)]
        exit_b.outgoing_edges = []

        func = MockFunction(start=0x1000, basic_blocks=[header, body, exit_b])
        cfg = CFGAnalysis(func)
        result = cfg.extract_function_cfg()
        assert result["cyclomatic_complexity"] == 2


class TestCFGExtractFunctionCFG:
    def _make_simple_cfg(self):
        """Create a simple two-block CFG for testing structure."""
        entry = MockBasicBlock(start=0x1000, end=0x1010)
        exit_b = MockBasicBlock(start=0x1010, end=0x1020)

        entry.outgoing_edges = [MockEdge(source=entry, target=exit_b, edge_type=BranchType.UnconditionalBranch)]
        exit_b.incoming_edges = [MockEdge(source=entry, target=exit_b)]
        exit_b.outgoing_edges = []
        entry.incoming_edges = []

        func = MockFunction(start=0x1000, basic_blocks=[entry, exit_b])
        return func

    def test_extract_function_cfg_structure(self):
        func = self._make_simple_cfg()
        cfg = CFGAnalysis(func)
        result = cfg.extract_function_cfg()
        assert "function_address" not in result
        # New schema: no "blocks" or "measures" nesting
        assert "blocks" not in result
        assert "measures" not in result

    def test_function_cfg_new_keys(self):
        """Assert all expected keys are present in the new output dict."""
        func = self._make_simple_cfg()
        cfg = CFGAnalysis(func)
        result = cfg.extract_function_cfg()
        expected_keys = [
            "cfg_topology_hash",
            "block_count",
            "edge_count",
            "llil_total_operations",
            "call_count",
            "cyclomatic_complexity",
            "loop_count",
            "max_depth",
            "max_fan_out",
            "md_index_topdown",
            "md_index_bottomup",
            "prime_product_llil",
            "cfg_feature_tlsh",
            "wl_minhash",
            "bb_features",
            "cfg_adjacency",
        ]
        for key in expected_keys:
            assert key in result, f"Missing key: {key}"

    def test_returns_none_for_empty_blocks(self):
        func = MockFunction(start=0x1000, basic_blocks=[])
        cfg = CFGAnalysis(func)
        assert cfg.extract_function_cfg() is None


class TestCFGTopologyHash:
    def _make_two_block_cfg(self):
        entry = MockBasicBlock(start=0x1000, end=0x1010)
        exit_b = MockBasicBlock(start=0x1010, end=0x1020)
        entry.outgoing_edges = [MockEdge(target=exit_b)]
        exit_b.outgoing_edges = []
        return MockFunction(start=0x1000, basic_blocks=[entry, exit_b])

    def test_topology_hash_is_16_bytes(self):
        func = self._make_two_block_cfg()
        cfg = CFGAnalysis(func)
        result = cfg.extract_function_cfg()
        assert isinstance(result["cfg_topology_hash"], bytes)
        assert len(result["cfg_topology_hash"]) == 16

    def test_topology_hash_deterministic(self):
        func = self._make_two_block_cfg()
        r1 = CFGAnalysis(func).extract_function_cfg()
        r2 = CFGAnalysis(func).extract_function_cfg()
        assert r1["cfg_topology_hash"] == r2["cfg_topology_hash"]


class TestCFGLoopCount:
    def test_no_loops(self):
        entry = MockBasicBlock(start=0x1000, end=0x1010)
        exit_b = MockBasicBlock(start=0x1010, end=0x1020)
        entry.outgoing_edges = [MockEdge(target=exit_b)]
        exit_b.outgoing_edges = []
        func = MockFunction(start=0x1000, basic_blocks=[entry, exit_b])
        result = CFGAnalysis(func).extract_function_cfg()
        assert result["loop_count"] == 0

    def test_single_loop(self):
        header = MockBasicBlock(start=0x1000, end=0x1010)
        body = MockBasicBlock(start=0x1010, end=0x1020)
        exit_b = MockBasicBlock(start=0x1020, end=0x1030)
        header.outgoing_edges = [MockEdge(target=body), MockEdge(target=exit_b)]
        body.outgoing_edges = [MockEdge(target=header)]
        exit_b.outgoing_edges = []
        func = MockFunction(start=0x1000, basic_blocks=[header, body, exit_b])
        result = CFGAnalysis(func).extract_function_cfg()
        assert result["loop_count"] == 1


class TestCFGMaxDepth:
    def test_max_depth_linear(self):
        entry = MockBasicBlock(start=0x1000, end=0x1010)
        b1 = MockBasicBlock(start=0x1010, end=0x1020)
        b2 = MockBasicBlock(start=0x1020, end=0x1030)
        entry.outgoing_edges = [MockEdge(target=b1)]
        b1.outgoing_edges = [MockEdge(target=b2)]
        b2.outgoing_edges = []
        func = MockFunction(start=0x1000, basic_blocks=[entry, b1, b2])
        result = CFGAnalysis(func).extract_function_cfg()
        assert result["max_depth"] == 2

    def test_max_depth_single_block(self):
        block = MockBasicBlock(start=0x1000, end=0x1010, outgoing_edges=[])
        func = MockFunction(start=0x1000, basic_blocks=[block])
        result = CFGAnalysis(func).extract_function_cfg()
        assert result["max_depth"] == 0


class TestCFGCollectBlockLlilOps:
    """Test that _collect_block_llil_ops correctly maps LLIL data to native blocks."""

    def test_llil_fields_nonzero_with_mock_llil(self):
        """When LLIL is available, llil_total_operations and call_count should be non-zero."""
        # Two native blocks
        entry = MockBasicBlock(start=0x1000, end=0x1010)
        exit_b = MockBasicBlock(start=0x1010, end=0x1020)
        entry.outgoing_edges = [MockEdge(target=exit_b)]
        exit_b.outgoing_edges = []

        # LLIL instructions: SET_REG, CALL in first block; STORE, RET in second
        llil_instrs_1 = [
            MockLLILInstruction(LowLevelILOperation.LLIL_SET_REG),
            MockLLILInstruction(LowLevelILOperation.LLIL_CALL),
        ]
        llil_instrs_2 = [
            MockLLILInstruction(LowLevelILOperation.LLIL_STORE),
            MockLLILInstruction(LowLevelILOperation.LLIL_RET),
        ]

        # LLIL basic blocks map back to native blocks via source_block
        llil_bb1 = MockLLILBasicBlock(llil_instrs_1, source_block=entry)
        llil_bb2 = MockLLILBasicBlock(llil_instrs_2, source_block=exit_b)
        llil_func = MockLLILFunction([llil_bb1, llil_bb2])

        func = MockFunction(start=0x1000, basic_blocks=[entry, exit_b], llil=llil_func)
        result = CFGAnalysis(func, llil_function=llil_func).extract_function_cfg()

        assert result["llil_total_operations"] == 4
        assert result["call_count"] == 1
        assert result["prime_product_llil"] != 0

    def test_llil_none_gives_zero_fields(self):
        """Without LLIL, LLIL-dependent fields should be zero."""
        block = MockBasicBlock(start=0x1000, end=0x1010, outgoing_edges=[])
        func = MockFunction(start=0x1000, basic_blocks=[block])
        result = CFGAnalysis(func).extract_function_cfg()

        assert result["llil_total_operations"] == 0
        assert result["call_count"] == 0
        assert result["prime_product_llil"] == 0

    def test_bb_features_with_llil(self):
        """bb_features should reflect LLIL instruction categories when LLIL is available."""
        block = MockBasicBlock(start=0x1000, end=0x1010, outgoing_edges=[])

        llil_instrs = [
            MockLLILInstruction(LowLevelILOperation.LLIL_ADD),
            MockLLILInstruction(LowLevelILOperation.LLIL_LOAD),
            MockLLILInstruction(LowLevelILOperation.LLIL_CALL),
        ]
        llil_bb = MockLLILBasicBlock(llil_instrs, source_block=block)
        llil_func = MockLLILFunction([llil_bb])

        func = MockFunction(start=0x1000, basic_blocks=[block])
        result = CFGAnalysis(func, llil_function=llil_func).extract_function_cfg()

        feats = result["bb_features"]
        assert len(feats) == 1
        assert feats[0][0] == 3  # instruction count = 3
        # At least one non-zero category count (not all OTHER)
        category_counts = feats[0][1:7]
        assert sum(category_counts) > 0


# ============================================================================
# 9b. DisassemblyAnalysis
# ============================================================================


class TestDisassemblyAnalysisGetJson:
    def _make_analysis(self, instructions=None, basic_blocks=None):
        arch = Arch_x86()
        if instructions is None:
            instructions = [
                (
                    [
                        MockToken("push", InstructionTextTokenType.InstructionToken),
                        MockToken(" ", InstructionTextTokenType.TextToken),
                        MockToken("rbp", InstructionTextTokenType.RegisterToken),
                    ],
                    0x1000,
                ),
                (
                    [
                        MockToken("mov", InstructionTextTokenType.InstructionToken),
                        MockToken(" ", InstructionTextTokenType.TextToken),
                        MockToken("rsp", InstructionTextTokenType.RegisterToken),
                    ],
                    0x1003,
                ),
            ]
        if basic_blocks is None:
            basic_blocks = [MockBasicBlock(
                start=0x1000, end=0x1010,
                disassembly_text=[MockDisassemblyLine([MockToken("push rbp")])]
            )]

        func = MockFunction(
            name="test_func",
            start=0x1000,
            basic_blocks=basic_blocks,
            instructions=instructions,
            symbol=MockSymbol(symbol_type=SymbolType.FunctionSymbol, name="test_func"),
            stack_adjustment=MagicMock(value=-8),
            mlil=None,
        )
        bv = MockBinaryView()
        logger = MagicMock()
        return DisassemblyAnalysis(arch, func, bv, logger)

    def test_get_json_basic_structure(self):
        da = self._make_analysis()
        result, errors = da.get_json()
        expected_keys = [
            "disassembled_function_hash",
            "disassembled_function",
            "disassembled_function_no_addresses",
            "disassembled_function_name",
            "disassembled_function_address",
            "instructions_count",
            "function_type",
            "instructions_types",
            "control_flow_count",
            "memory_access_pattern",
            "register_usage",
            "data_references_count",
        ]
        for key in expected_keys:
            assert key in result, f"Missing key: {key}"

    def test_get_json_hash_deterministic(self):
        da = self._make_analysis()
        r1, _ = da.get_json()
        da2 = self._make_analysis()
        r2, _ = da2.get_json()
        assert r1["disassembled_function_hash"] == r2["disassembled_function_hash"]


class TestDisassemblyCollectInstructionTypes:
    def test_collect_instruction_types(self):
        arch = Arch_x86()
        instructions = [
            ([MockToken("MOV", InstructionTextTokenType.InstructionToken)], 0x1000),
            ([MockToken("ADD", InstructionTextTokenType.InstructionToken)], 0x1001),
            ([MockToken("MOV", InstructionTextTokenType.InstructionToken)], 0x1002),
        ]
        func = MockFunction(start=0x1000, instructions=instructions, symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        types = da.collect_instruction_types()
        assert "DATA_MOVEMENT" in types
        assert "ARITHMETIC" in types

    def test_collect_instruction_types_empty(self):
        arch = Arch_x86()
        func = MockFunction(start=0x1000, instructions=[], symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        types = da.collect_instruction_types()
        assert types == {}


class TestDisassemblyMemoryPatterns:
    def _make_memory_instruction(self, tokens):
        return ([t for t in tokens], 0x1000)

    def test_collect_memory_patterns_stack(self):
        arch = Arch_x86()
        tokens = [
            MockToken("[", InstructionTextTokenType.BeginMemoryOperandToken),
            MockToken("RSP", InstructionTextTokenType.RegisterToken),
            MockToken("+0x8", InstructionTextTokenType.TextToken),
            MockToken("]", InstructionTextTokenType.EndMemoryOperandToken),
        ]
        instructions = [self._make_memory_instruction(tokens)]
        func = MockFunction(start=0x1000, instructions=instructions, symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        patterns = da.collect_memory_patterns()
        assert "MEM_STACK" in patterns

    def test_collect_memory_patterns_direct(self):
        arch = Arch_x86()
        tokens = [
            MockToken("[", InstructionTextTokenType.BeginMemoryOperandToken),
            MockToken("0x402000", InstructionTextTokenType.TextToken),
            MockToken("]", InstructionTextTokenType.EndMemoryOperandToken),
        ]
        instructions = [self._make_memory_instruction(tokens)]
        func = MockFunction(start=0x1000, instructions=instructions, symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        patterns = da.collect_memory_patterns()
        assert "MEM_DIRECT" in patterns

    def test_collect_memory_patterns_scaled(self):
        arch = Arch_x86()
        tokens = [
            MockToken("[", InstructionTextTokenType.BeginMemoryOperandToken),
            MockToken("RAX+RCX*4", InstructionTextTokenType.TextToken),
            MockToken("]", InstructionTextTokenType.EndMemoryOperandToken),
        ]
        instructions = [self._make_memory_instruction(tokens)]
        func = MockFunction(start=0x1000, instructions=instructions, symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        patterns = da.collect_memory_patterns()
        assert "MEM_SCALED_INDEX" in patterns

    def test_collect_memory_patterns_base_offset(self):
        arch = Arch_x86()
        tokens = [
            MockToken("[", InstructionTextTokenType.BeginMemoryOperandToken),
            MockToken("RAX+0x10", InstructionTextTokenType.TextToken),
            MockToken("]", InstructionTextTokenType.EndMemoryOperandToken),
        ]
        instructions = [self._make_memory_instruction(tokens)]
        func = MockFunction(start=0x1000, instructions=instructions, symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        patterns = da.collect_memory_patterns()
        assert "MEM_BASE_OFFSET" in patterns


class TestDisassemblyRegisterUsage:
    def test_collect_register_usage_gpr(self):
        arch = Arch_x86()
        instructions = [
            ([MockToken("RAX", InstructionTextTokenType.RegisterToken)], 0x1000),
        ]
        func = MockFunction(start=0x1000, instructions=instructions, symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        regs = da.collect_register_usage()
        assert "GPR" in regs

    def test_collect_register_usage_simd(self):
        arch = Arch_x86()
        instructions = [
            ([MockToken("XMM0", InstructionTextTokenType.RegisterToken)], 0x1000),
        ]
        func = MockFunction(start=0x1000, instructions=instructions, symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        regs = da.collect_register_usage()
        assert "SIMD" in regs

    def test_collect_register_usage_fpu(self):
        arch = Arch_x86()
        instructions = [
            ([MockToken("ST0", InstructionTextTokenType.RegisterToken)], 0x1000),
        ]
        func = MockFunction(start=0x1000, instructions=instructions, symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        regs = da.collect_register_usage()
        assert "FPU" in regs


class TestDisassemblyMisc:
    def test_count_data_references(self):
        arch = Arch_x86()
        func = MockFunction(start=0x1000, instructions=[], symbol=MockSymbol(), mlil=None)
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        assert da.count_data_references() == 0

    def test_compute_max_block_size(self):
        arch = Arch_x86()
        blocks = [
            MockBasicBlock(disassembly_text=[MockDisassemblyLine([]) for _ in range(3)]),
            MockBasicBlock(disassembly_text=[MockDisassemblyLine([]) for _ in range(5)]),
        ]
        func = MockFunction(start=0x1000, basic_blocks=blocks, instructions=[], symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        assert da.compute_max_block_size() == 5

    def test_compute_num_calls(self):
        arch = Arch_x86()
        instructions = [
            ([MockToken("CALL", InstructionTextTokenType.InstructionToken)], 0x1000),
            ([MockToken("MOV", InstructionTextTokenType.InstructionToken)], 0x1005),
            ([MockToken("CALL", InstructionTextTokenType.InstructionToken)], 0x1010),
        ]
        func = MockFunction(start=0x1000, instructions=instructions, symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        assert da.compute_num_calls() == 2

    def test_estimate_stack_size_value(self):
        arch = Arch_x86()
        stack = MagicMock()
        stack.value = -16
        func = MockFunction(start=0x1000, instructions=[], symbol=MockSymbol(), stack_adjustment=stack)
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        assert da.estimate_stack_size() == -16

    def test_estimate_stack_size_int(self):
        arch = Arch_x86()
        func = MockFunction(start=0x1000, instructions=[], symbol=MockSymbol(), stack_adjustment=-8)
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        assert da.estimate_stack_size() == -8

    def test_normalize_opcode(self):
        arch = Arch_x86()
        func = MockFunction(start=0x1000, instructions=[], symbol=MockSymbol())
        da = DisassemblyAnalysis(arch, func, MockBinaryView(), MagicMock())
        assert da.normalize_opcode("mov") == "MOV"
        assert da.normalize_opcode("PUSH") == "PUSH"


# ============================================================================
# 9c. LowLevelAnalysis (basic tests with mocked LLIL)
# ============================================================================


class MockLLILInstruction:
    """Mock LLIL instruction for low_level.py tests."""
    def __init__(self, operation, operands=None, address=0):
        self.operation = operation
        self.operands = operands or []
        self.address = address

    def __str__(self):
        return f"LLIL_{self.operation}"


class MockLLILBasicBlock:
    def __init__(self, instructions, source_block=None):
        self._instructions = instructions
        self.source_block = source_block

    def __iter__(self):
        return iter(self._instructions)


class MockLLILFunction:
    def __init__(self, basic_blocks):
        self.basic_blocks = basic_blocks
        self._instructions = []
        for bb in basic_blocks:
            self._instructions.extend(bb._instructions)

    @property
    def instructions(self):
        return iter(self._instructions)

    @property
    def source_function(self):
        mock = MagicMock()
        mock.start = 0x1000
        return mock


class TestLowLevelAnalysisCountControlFlow:
    def test_count_control_flow_instructions(self):
        from redb.extractors.decompiler.bninja.analysis.low_level import LowLevelAnalysis
        instrs = [
            MockLLILInstruction(LowLevelILOperation.LLIL_IF),
            MockLLILInstruction(LowLevelILOperation.LLIL_SET_REG),
            MockLLILInstruction(LowLevelILOperation.LLIL_CALL),
            MockLLILInstruction(LowLevelILOperation.LLIL_GOTO),
        ]
        bb = MockLLILBasicBlock(instrs)
        llil_func = MockLLILFunction([bb])

        func = MockFunction(start=0x1000, llil=llil_func, symbol=MockSymbol())
        func.low_level_il = None
        bv = MockBinaryView()
        bv.arch = MagicMock()
        bv.arch.stack_pointer = "sp"
        la = LowLevelAnalysis(func, bv, MagicMock())
        assert la.count_control_flow_instructions() == 3  # IF, CALL, GOTO


class TestLowLevelAnalysisNumCalls:
    def test_compute_num_calls_llil(self):
        from redb.extractors.decompiler.bninja.analysis.low_level import LowLevelAnalysis
        instrs = [
            MockLLILInstruction(LowLevelILOperation.LLIL_CALL),
            MockLLILInstruction(LowLevelILOperation.LLIL_TAILCALL),
            MockLLILInstruction(LowLevelILOperation.LLIL_SET_REG),
        ]
        bb = MockLLILBasicBlock(instrs)
        llil_func = MockLLILFunction([bb])

        func = MockFunction(start=0x1000, llil=llil_func, symbol=MockSymbol())
        func.low_level_il = None
        bv = MockBinaryView()
        la = LowLevelAnalysis(func, bv, MagicMock())
        assert la.compute_num_calls() == 2


class TestLowLevelAnalysisCollectNormalization:
    def test_collect_low_level(self):
        from redb.extractors.decompiler.bninja.analysis.low_level import LowLevelAnalysis
        instrs = [
            MockLLILInstruction(LowLevelILOperation.LLIL_SET_REG, address=0x1000),
            MockLLILInstruction(LowLevelILOperation.LLIL_STORE, address=0x1004),
        ]
        bb = MockLLILBasicBlock(instrs)
        llil_func = MockLLILFunction([bb])

        func = MockFunction(start=0x1000, llil=llil_func, symbol=MockSymbol())
        func.low_level_il = None
        bv = MockBinaryView()
        la = LowLevelAnalysis(func, bv, MagicMock())
        result, _ = la._collect_low_level_and_with_addr()
        assert len(result) == 2
        # Each item is a list of operation ints
        assert isinstance(result[0], list)

    def test_collect_low_level_with_addr_offset_clamping(self):
        from redb.extractors.decompiler.bninja.analysis.low_level import LowLevelAnalysis
        instrs = [
            MockLLILInstruction(LowLevelILOperation.LLIL_SET_REG, address=0x0FFF),  # Before function start
        ]
        bb = MockLLILBasicBlock(instrs)
        llil_func = MockLLILFunction([bb])

        func = MockFunction(start=0x1000, llil=llil_func, symbol=MockSymbol())
        func.low_level_il = None
        bv = MockBinaryView()
        la = LowLevelAnalysis(func, bv, MagicMock())
        _, result = la._collect_low_level_and_with_addr()
        assert len(result) == 1
        offset, _ = result[0]
        assert offset == 0  # Clamped to 0