Neil Y. Yen

122 papers A* 2B 10C 1Misc 1Journal 78Unranked 26
YearRankTypeTitle / Venue / Authors
2025 J jnl
IEEE Access
Lei Jia, Neil Y. Yen, Yan Pei
2025 J jnl
Multim. Tools Appl.
Byeong-Seok Shin, Neil Y. Yen, Jong Hyuk Park, Young-Sik Jeong
2024 J jnl
Comput. Sci. Inf. Syst.
Yenjou Wang, Jason C. Hung, Chun-Hong Huang, Sadiq Hussain, Neil Y. Yen, Qun Jin
2024 J jnl
J. Ambient Intell. Humaniz. Comput.
Jason C. Hung, Neil Y. Yen, Francisco Isidro Massetto
2024 J jnl
Comput. Commun.
Zheng Xu, Neil Y. Yen, Xiaomeng Ma, Vijayan Sugumaran, Yunhuai Liu
2024 J jnl
J. Ambient Intell. Humaniz. Comput.
Neil Y. Yen, Chia-Chen Chen, Jia-Jiun Hu
2024 B conf
SMC
Lei Jia, Neil Y. Yen, Yan Pei
2024 B conf
SMC
Lei Jia, Yan Pei, Neil Y. Yen
2024 conf
BCD
Lei Jia, Neil Y. Yen, Yan Pei
2023 J jnl
IEEE Trans. Geosci. Remote. Sens.
Lei Jia, Neil Y. Yen, Yan Pei
2022 conf
HCI (14)
Yenjou Wang, Neil Y. Yen, Qun Jin
2022 J jnl
J. Ambient Intell. Humaniz. Comput.
Young-Sik Jeong, Jin Wang, Neil Y. Yen
2021 J jnl
Comput. Sci. Inf. Syst.
Neil Y. Yen, Hwa-Young Jeong, Kurosh Madani, Francisco Isidro Massetto
2021 J jnl
Neural Comput. Appl.
Chuanchao Huang, Yu-Wei Chan, Neil Y. Yen
2020 J jnl
J. Supercomput.
Neil Y. Yen, Jia-Wei Chang, Jia-Yi Liao, You-Ming Yong
2020 J jnl
Comput. Hum. Behav.
Moloud Abdar, Neil Y. Yen
2020 J jnl
IEEE Trans. Cloud Comput.
Zheng Xu, Yunhuai Liu, Neil Y. Yen, Lin Mei, Xiangfeng Luo, Xiao Wei, Chuanping Hu
2020 ed.
DPTA
Chuanchao Huang, Yu-Wei Chan, Neil Y. Yen
2020 J jnl
IEEE Trans. Comput. Soc. Syst.
Yulei Wu, Fei Hao, Juanjuan Li, Neil Y. Yen, Yi Pan, Victor C. M. Leung
2020 J jnl
J. Supercomput.
Jun-Hong Shen, Ching-Ta Lu, Mu-Yen Chen, Neil Y. Yen
2020 J jnl
IEEE Trans. Comput. Soc. Syst.
Mohammad Ehsan Basiri, Moloud Abdar, Arman Kabiri, Shahla Nemati, Xujuan Zhou, Forough Allahbakhshi, Neil Y. Yen
2020 J jnl
J. Supercomput.
Abdul Shahid, Muhammad Tanvir Afzal, Moloud Abdar, Mohammad Ehsan Basiri, Xujuan Zhou, Neil Y. Yen, Jia-Wei Chang
2020 J jnl
Soft Comput.
Chao-Tung Yang, Shuo-Tsung Chen, Jung-Chun Liu, Pei-Lun Sun, Neil Y. Yen
2020 J jnl
J. Supercomput.
Jun-Hong Shen, Cheng-Jung Yu, Ching-Ta Lu, WenYen Lin, Neil Y. Yen, Tien-Chi Huang, Hong-Ray Chu
2020 J jnl
Enterp. Inf. Syst.
Mohammad Ehsan Basiri, Arman Kabiri, Moloud Abdar, Wali Khan Mashwani, Neil Y. Yen, Jason C. Hung
2019 J jnl
IEEE Access
Shahla Nemati, Reza Rohani, Mohammad Ehsan Basiri, Moloud Abdar, Neil Y. Yen, Vladimir Makarenkov
2019 J jnl
Comput. Hum. Behav.
Neil Y. Yen, Jason C. Hung, Chia-Chen Chen, Qun Jin
2019 conf
ICCI*CC
Yuya Hatakeyama, Runhe Huang, Bowen Du, Neil Y. Yen, Haiquan Wang
2019 J jnl
Neural Comput. Appl.
Zheng Xu, Neil Y. Yen
2019 J jnl
J. Ambient Intell. Humaniz. Comput.
Zheng Xu, Yunhuai Liu, Neil Y. Yen
2019 J jnl
Comput. Hum. Behav.
Qingyuan Zhou, Zheng Xu, Neil Y. Yen
2018 J jnl
J. Parallel Distributed Comput.
Wuhui Chen, Incheon Paik, Zhenni Li, Neil Y. Yen
2018 J jnl
J. Ambient Intell. Humaniz. Comput.
Jong Hyuk Park, Neil Y. Yen
2018 conf
DASC/PiCom/DataCom/CyberSciTech
Kuan-Hua Lai, Neil Y. Yen, Mu-Yen Chen
2018 J jnl
Appl. Soft Comput.
Heng Lee, Moloud Abdar, Neil Y. Yen
2018 J jnl
IEEE Trans. Big Data
Zheng Xu, Xiangfeng Luo, Yunhuai Liu, Kim-Kwang Raymond Choo, Vijayan Sugumaran, Neil Y. Yen, Lin Mei, Chuanping Hu
2018 conf
WWW (Companion Volume)
Mu-Yen Chen, Tien-Chi Huang, Vera Yu Shu, Chia-Chen Chen, Tsung-Che Hsieh, Neil Y. Yen
2018 J jnl
Neurocomputing
Neil Y. Yen, Ching-Hsien Hsu, Qun Jin, Odej Kao
2018 J jnl
Electron. Commer. Res.
Zheng Xu, Vijayan Sugumaran, Neil Y. Yen
2017 conf
AINA Workshops
Moloud Abdar, Neil Y. Yen
2017 conf
CYBCONF
Moloud Abdar, Kuan-Hua Lai, Neil Y. Yen
2017 J jnl
IEEE Access
Moloud Abdar, Neil Y. Yen
2017 J jnl
Multim. Tools Appl.
Wuhui Chen, Incheon Paik, Neil Y. Yen
2017 J jnl
Mob. Networks Appl.
Zheng Xu, Yunhuai Liu, Neil Y. Yen
2017 J jnl
Libr. Hi Tech
Mu-Yen Chen, Edwin Lughofer, Neil Y. Yen, Chia-Chen Chen
2017 J jnl
IEEE Access
Weiyao Lin, Zheng Xu, Yunhuai Liu, Neil Y. Yen, Kim-Kwang Raymond Choo, Vijayan Sugumaran
2017 J jnl
Int. J. Soc. Humanist. Comput.
Moloud Abdar, Neil Y. Yen
2017 J jnl
IEEE Trans. Emerg. Top. Comput.
Zheng Xu, Neil Y. Yen, Hui Zhang, Xiao Wei, Zhihan Lv, Kim-Kwang Raymond Choo, Lin Mei, Xiangfeng Luo
2017 J jnl
Wirel. Pers. Commun.
Zheng Xu, Yunhuai Liu, Kim-Kwang Raymond Choo, Neil Y. Yen
2017 J jnl
Concurr. Comput. Pract. Exp.
Zheng Xu, Neil Y. Yen, Vijayan Sugumaran
2017 J jnl
Mob. Networks Appl.
Shunxiang Zhang, Shiyao Zhang, Neil Y. Yen, Guangli Zhu
2017 J jnl
Multim. Tools Appl.
Runhe Huang, Atsushi Sato, Toshihiro Tamura, Jianhua Ma, Neil Y. Yen
2017 J jnl
Libr. Hi Tech
Moloud Abdar, Neil Y. Yen
2016 J jnl
Int. J. Web Grid Serv.
Chun-Hao Chen, Vincent S. Tseng, Hsieh-Hui Yu, Tzung-Pei Hong, Neil Y. Yen
2016 conf
CBD
Zixue Cheng, Junbo Wang, Neil Y. Yen, Yilang Wu
2016 J jnl
IEEE Commun. Surv. Tutorials
Junbo Wang, Yilang Wu, Neil Y. Yen, Song Guo, Zixue Cheng
2016 J jnl
Neurocomputing
Neil Y. Yen, Fatos Xhafa, Albert Y. Zomaya
2016 J jnl
J. Supercomput.
Kuan-Cheng Lin, Kaiyuan Zhang, Yi-Hung Huang, Jason C. Hung, Neil Y. Yen
2016 J jnl
Multim. Tools Appl.
Jong Hyuk Park, Young-Sik Jeong, Gangman Yi, Neil Y. Yen
2016 conf
DASC/PiCom/DataCom/CyberSciTech
Xiaokang Zhou, Bo Wu, Qun Jin, Jianhua Ma, Weimin Li, Neil Y. Yen
2016 J jnl
Future Gener. Comput. Syst.
Goldina Ghosh, Soumya Banerjee, Neil Y. Yen
2016 J jnl
J. Medical Syst.
Ching-Sheng Wang, Lun-Ping Hung, Neil Y. Yen
2016 ch.
Semantic Web Technologies for Intelligent Engineering Applications
Jianhua Ma, Neil Y. Yen, Runhe Huang, Xin Zhao
2015 J jnl
Multim. Tools Appl.
Jason C. Hung, Neil Y. Yen, Hwa-Young Jeong, Yu-Wei Chan
2015 conf
ESaaSA@CLOSER
Neil Y. Yen, Jason C. Hung
2015 J jnl
Hum. centric Comput. Inf. Sci.
Atsushi Sato, Runhe Huang, Neil Y. Yen
2015 J jnl
Int. J. Distributed Sens. Networks
Neil Y. Yen, Odej Kao, Hai Jiang, Jason C. Hung
2015 conf
NBiS
Eiman Tamah Al-Shammari, Soumya Banerjee, Dana Balas-Timar, Neil Y. Yen
2015 J jnl
Multim. Tools Appl.
Neil Y. Yen, Qun Jin, Joseph C. Tsai, James J. Park
2015 J jnl
Comput. J.
Neil Y. Yen, Uyen Trang Nguyen, Jong Hyuk Park
2015 J jnl
ACM Comput. Surv.
Bin Guo, Zhu Wang, Zhiwen Yu, Yu Wang, Neil Y. Yen, Runhe Huang, Xingshe Zhou
2015 J jnl
Int. J. Distributed Sens. Networks
Young-Sik Jeong, Hyun-Woo Kim, Neil Y. Yen, Jong Hyuk Park
2015 ed.
HIC@HT
Neil Y. Yen, Qun Jin, Zheng Xu
2015 J jnl
Multim. Syst.
Chia-Chen Chen, Tien-Chi Huang, James J. Park, Neil Y. Yen
2015 J jnl
Multim. Tools Appl.
Neil Y. Yen, Chengcui Zhang, Agustinus Borgy Waluyo, James J. Park
2015 B conf
AINA
Neil Y. Yen, Jason C. Hung
2014 J jnl
Pers. Ubiquitous Comput.
Chia-Chen Chen, Tien-Chi Huang, James J. Park, Huang-Hua Tseng, Neil Y. Yen
2014 J jnl
J. Supercomput.
Neil Y. Yen, Cho-Li Wang, Sajid Hussain, Jong Hyuk Park
2014 J jnl
Int. J. Ad Hoc Ubiquitous Comput.
Lun-Ping Hung, James J. Park, Kuo-Chung Chu, Neil Y. Yen
2014 B conf
SMC
Neil Y. Yen, Runhe Huang, Jianhua Ma
2014 J jnl
Int. J. Distance Educ. Technol.
Martin M. Weng, Wen-Chih Chang, Neil Y. Yen, Timothy K. Shih, Hui-Huang Hsu
2014 J jnl
Int. J. Distributed Sens. Networks
Nan-Chen Hsieh, Lun-Ping Hung, Jong Hyuk Park, Neil Y. Yen
2014 J jnl
Int. J. Distributed Sens. Networks
Jong Hyuk Park, Han-Chieh Chao, Sajid Hussain, Neil Y. Yen
2014 J jnl
World Wide Web
Rynson W. H. Lau, Neil Y. Yen, Frederick W. B. Li, Benjamin W. Wah
2014 conf
ISIC
Joseph C. Tsai, Neil Y. Yen, Takafumi Hayashi
2014 J jnl
Future Gener. Comput. Syst.
Neil Y. Yen, Qin Jin, Ching-Hsien Hsu, Qiangfu Zhao
2013 conf
EMC/HumanCom
Zixue Cheng, Junbo Wang, T. Huang, P. Li, Neil Y. Yen, Joseph C. Tsai, Yinghui Zhou, Lei Jing
2013 J jnl
J. Adv. Comput. Intell. Intell. Informatics
Yuya Kaneda, Qiangfu Zhao, Yong Liu, Neil Y. Yen
2013 conf
CYBCONF
Neil Y. Yen, Qiangfu Zhao, Yong Liu, Joseph C. Tsai
2013 J jnl
J. Syst. Archit.
Lei Jing, Yinghui Zhou, Zixue Cheng, Neil Y. Yen, James J. Park
2013 J jnl
Multim. Tools Appl.
Xiaokang Zhou, Neil Y. Yen, Qun Jin, Timothy K. Shih
2013 conf
UIC/ATC
Guangyu Piao, Qun Jin, Xiaokang Zhou, Shoji Nishimura, Kanoksak Wattanachote, Timothy K. Shih, Neil Y. Yen
2013 J jnl
Libr. Hi Tech
Kuan-Cheng Lin, Tien-Chi Huang, Jason C. Hung, Neil Y. Yen, Szu Ju Chen
2013 B conf
SMC
Qiangfu Zhao, Yutaro Minakawa, Yong Liu, Neil Y. Yen
2013 conf
iCAST/UMEDIA
Yuya Kaneda, Qiangfu Zhao, Yong Liu, Neil Y. Yen
2013 B conf
SMC
Yuya Kaneda, Qiangfu Zhao, Yong Liu, Neil Y. Yen
2013 J jnl
Int. J. Commun. Syst.
Neil Y. Yen, Runhe Huang, Jianhua Ma, Qun Jin, Timothy K. Shih
2013 J jnl
ACM Trans. Intell. Syst. Technol.
Neil Y. Yen, Timothy K. Shih, Qun Jin
2013 conf
iCAST/UMEDIA
Yong Liu, Qiangfu Zhao, Neil Y. Yen
2013 J jnl
Pers. Ubiquitous Comput.
Neil Y. Yen, Jong Hyuk Park, Qun Jin, Timothy K. Shih
2013 conf
GreenCom/iThings/CPScom
Atsushi Sato, Toshihiro Tamura, Runhe Huang, Jianhua Ma, Neil Y. Yen
2013 B conf
SMC
Yong Liu, Qiangfu Zhao, Neil Y. Yen
2013 conf
UIC/ATC
Jingwei Wang, Neil Y. Yen, Bin Guo, Runhe Huang, Jianhua Ma, Tao Ban, Hong Zhao
2012 ed.
AMT
Runhe Huang, Ali A. Ghorbani, Gabriella Pasi, Takahira Yamaguchi, Neil Y. Yen, Beijing Jin
2012 B conf
ICALT
Martin M. Weng, Bruce C. Kau, Neil Y. Yen
2012 conf
NBiS
Martin M. Weng, Guangyu Piao, Neil Y. Yen, Timothy K. Shih
2012 conf
Web Intelligence
Martin M. Weng, Neil Y. Yen, Jason C. Hung, Timothy K. Shih
2012 B conf
SMC
Yong Liu, Qiangfu Zhao, Neil Y. Yen
2011 J jnl
J. Multim.
Neil Y. Yen, Timothy K. Shih, Qun Jin, Jason C. Hung, Qingguo Zhou, Louis R. Chao
2011 conf
ICWL
Neil Y. Yen, Timothy K. Shih, Qun Jin, Li-Chieh Lin
2011 Misc conf
GrC
Neil Y. Yen, Qun Jin, Timothy K. Shih, Li-Chieh Lin
2011 C conf
ISADS
Qun Jin, Yishui Zhu, Roman Y. Shtykh, Neil Y. Yen, Timothy K. Shih
2011 A* conf
ACM Multimedia
Rynson W. H. Lau, Timothy K. Shih, Frederick W. B. Li, Neil Y. Yen
2010 conf
ICWL
Jian Chen, Haifeng Man, Neil Y. Yen, Qun Jin, Timothy K. Shih
2010 J jnl
IEEE Trans. Learn. Technol.
Neil Y. Yen, Timothy K. Shih, Louis R. Chao, Qun Jin
2010 J jnl
J. Softw.
Neil Y. Yen, Jason C. Hung, Hui-Huang Hsu, Timothy K. Shih, Louis R. Chao
2010 conf
GreenCom/CPSCom
Neil Y. Yen, Jianhua Ma, Runhe Huang, Qun Jin, Timothy K. Shih
2010 J jnl
Int. J. Distance Educ. Technol.
Neil Y. Yen, Timothy K. Shih, Qun Jin, Hui-Huang Hsu, Louis R. Chao
2009 A* conf
ACM Multimedia
Timothy K. Shih, Rynson W. H. Lau, Neil Y. Yen
2009 conf
ICHL
Neil Y. Yen, Timothy K. Shih, Louis R. Chao
2009 B conf
ICALT
Neil Y. Yen, Franz F. Hou, Louis R. Chao, Timothy K. Shih
2007 conf
ICPP Workshops
Te-Hua Wang, Neil Y. Yen, Yue-Lin Du, Timothy K. Shih
tests/unit/test_decompile_modules.py
← Index tests/unit/test_decompile_modules.py python
"""
Unit tests for --decompile-modules flag.

Tests that:
1. CLI argument parsing and validation works correctly
2. decompile_modules is threaded through the entire call chain
3. BinaryNinjaDecompiler.analyze_binary() gates extractions per selected modules
4. DecompileBinja.prepare_export_data() filters DB tables per selected modules
"""
import os
import pytest
from unittest.mock import Mock, patch, MagicMock

pytestmark = [pytest.mark.unit]


# ============================================================================
# CLI Argument Parsing Tests
# ============================================================================

class TestDecompileModulesCliArgument:
    """Tests for --decompile-modules CLI argument parsing in start.py."""

    def _parse_args(self, args_list):
        """Helper to parse CLI args without running main()."""
        import argparse

        parser = argparse.ArgumentParser()
        input_group = parser.add_mutually_exclusive_group(required=True)
        input_group.add_argument("--path")
        input_group.add_argument("--s3", action="store_true")
        input_group.add_argument("--s3-solo", metavar="S3_KEY")
        input_group.add_argument("--nomad-job", action="store_true")
        input_group.add_argument("--date", metavar="YYYY-MM-DD")
        input_group.add_argument("--range", nargs=2)
        input_group.add_argument("--analyzed", action="store_true")

        parser.add_argument("--repo")
        parser.add_argument("--index_prefix", default="redb")
        parser.add_argument("-d", "--decompile", action="store_true")
        parser.add_argument("-m", "--modules", default="all")
        parser.add_argument("--decompile-modules", default="all")
        parser.add_argument("--force", action="store_true")
        parser.add_argument("--dry-run", action="store_true")

        return parser.parse_args(args_list)

    def test_decompile_modules_default_all(self):
        """--decompile-modules defaults to 'all'."""
        args = self._parse_args(["--path", "/tmp/test", "--repo", "test"])
        assert args.decompile_modules == "all"

    def test_decompile_modules_single(self):
        """--decompile-modules accepts a single module."""
        args = self._parse_args([
            "--path", "/tmp/test", "--repo", "test",
            "-d", "--decompile-modules", "strings"
        ])
        assert args.decompile_modules == "strings"

    def test_decompile_modules_comma_separated(self):
        """--decompile-modules accepts comma-separated modules."""
        args = self._parse_args([
            "--path", "/tmp/test", "--repo", "test",
            "-d", "--decompile-modules", "cfg,llil,strings"
        ])
        assert args.decompile_modules == "cfg,llil,strings"

    def test_decompile_modules_with_decompile_flag(self):
        """--decompile-modules works with -d flag."""
        args = self._parse_args([
            "--path", "/tmp/test", "--repo", "test",
            "-d", "--decompile-modules", "disassembly"
        ])
        assert args.decompile is True
        assert args.decompile_modules == "disassembly"


# ============================================================================
# Validation Tests
# ============================================================================

class TestDecompileModulesValidation:
    """Tests for --decompile-modules validation logic in start.py."""

    def test_valid_modules_set_parsing(self):
        """Valid comma-separated modules are parsed into a set."""
        VALID = {"all", "decompilation", "disassembly", "cfg", "llil", "strings"}
        raw = "cfg,llil,strings"
        modules_set = {m.strip() for m in raw.split(",")}
        invalid = modules_set - VALID
        assert invalid == set()
        assert modules_set == {"cfg", "llil", "strings"}

    def test_invalid_module_detected(self):
        """Invalid module names are detected."""
        VALID = {"all", "decompilation", "disassembly", "cfg", "llil", "strings"}
        raw = "cfg,invalid_module"
        modules_set = {m.strip() for m in raw.split(",")}
        invalid = modules_set - VALID
        assert invalid == {"invalid_module"}

    def test_all_keyword_as_set(self):
        """'all' is parsed into {'all'} set."""
        raw = "all"
        if raw == "all":
            modules_set = {"all"}
        assert modules_set == {"all"}

    def test_whitespace_handling(self):
        """Whitespace around module names is stripped."""
        raw = " cfg , llil , strings "
        modules_set = {m.strip() for m in raw.split(",")}
        assert modules_set == {"cfg", "llil", "strings"}


# ============================================================================
# Ingestor Parameter Storage Tests
# ============================================================================

class TestIngestorDecompileModules:
    """Tests that Ingestor stores decompile_modules correctly."""

    @patch('builtins.open', MagicMock())
    @patch('redb.ingestor.multiprocessing')
    @patch('redb.ingestor.datetime')
    def test_ingestor_stores_decompile_modules(self, mock_datetime, mock_mp):
        """Ingestor stores the decompile_modules set."""
        mock_mp.Manager.return_value.dict = Mock(side_effect=[{}, {}])
        mock_datetime.today.return_value.strftime.return_value = "20260222"

        from redb.ingestor import Ingestor
        ingestor = Ingestor(
            path="/tmp/test", repository="test", index_prefix="redb",
            decompile_modules={"cfg", "strings"}
        )
        assert ingestor.decompile_modules == {"cfg", "strings"}

    @patch('builtins.open', MagicMock())
    @patch('redb.ingestor.multiprocessing')
    @patch('redb.ingestor.datetime')
    def test_ingestor_defaults_to_all(self, mock_datetime, mock_mp):
        """Ingestor defaults decompile_modules to {'all'} when not provided."""
        mock_mp.Manager.return_value.dict = Mock(side_effect=[{}, {}])
        mock_datetime.today.return_value.strftime.return_value = "20260222"

        from redb.ingestor import Ingestor
        ingestor = Ingestor(
            path="/tmp/test", repository="test", index_prefix="redb"
        )
        assert ingestor.decompile_modules == {"all"}

    @patch('builtins.open', MagicMock())
    @patch('redb.ingestor.multiprocessing')
    @patch('redb.ingestor.datetime')
    def test_ingestor_none_defaults_to_all(self, mock_datetime, mock_mp):
        """Ingestor converts None decompile_modules to {'all'}."""
        mock_mp.Manager.return_value.dict = Mock(side_effect=[{}, {}])
        mock_datetime.today.return_value.strftime.return_value = "20260222"

        from redb.ingestor import Ingestor
        ingestor = Ingestor(
            path="/tmp/test", repository="test", index_prefix="redb",
            decompile_modules=None
        )
        assert ingestor.decompile_modules == {"all"}


# ============================================================================
# Function Chain Threading Tests
# ============================================================================

class TestDecompileModulesThreading:
    """Tests that decompile_modules is correctly threaded through the call chain."""

    @patch('redb.workers.process_binary_file')
    @patch('redb.workers.is_binary_file', return_value=True)
    def test_process_file_internal_passes_decompile_modules(self, mock_is_binary, mock_process):
        """_process_file_internal passes decompile_modules to process_binary_file."""
        from redb.ingestor import _process_file_internal

        mock_process.return_value = (Mock(), "pebin")
        logger = Mock()
        modules = {"cfg", "strings"}

        _process_file_internal(
            "/tmp/test.bin", False, "redb", logger,
            decompile_modules=modules
        )

        mock_process.assert_called_once()
        call_kwargs = mock_process.call_args[1]
        assert call_kwargs["decompile_modules"] == modules

    @patch('redb.workers.process_binary_file')
    @patch('redb.workers.is_binary_file', return_value=True)
    def test_process_file_internal_default_decompile_modules_none(self, mock_is_binary, mock_process):
        """_process_file_internal defaults decompile_modules to None."""
        from redb.ingestor import _process_file_internal

        mock_process.return_value = (Mock(), "pebin")
        logger = Mock()

        _process_file_internal("/tmp/test.bin", False, "redb", logger)

        call_kwargs = mock_process.call_args[1]
        assert call_kwargs["decompile_modules"] is None

    @patch('redb.workers._process_file_internal')
    def test_process_file_passes_decompile_modules(self, mock_internal):
        """process_file passes decompile_modules to _process_file_internal."""
        from redb.workers import process_file

        mock_internal.return_value = (Mock(), "pebin")
        modules = {"llil", "disassembly"}

        with patch('redb.workers.setup_direct_logger', return_value=Mock()):
            process_file(
                "/tmp/test.bin", False, "redb", "/tmp/log", 1, 1,
                decompile_modules=modules
            )

        mock_internal.assert_called_once()
        call_kwargs = mock_internal.call_args[1]
        assert call_kwargs["decompile_modules"] == modules

    @patch('redb.workers._process_file_internal')
    def test_process_s3_file_passes_decompile_modules(self, mock_internal):
        """process_s3_file passes decompile_modules to _process_file_internal."""
        from redb.workers import process_s3_file

        mock_internal.return_value = (Mock(), "pebin")
        modules = {"cfg"}

        with patch('redb.workers.setup_direct_logger', return_value=Mock()), \
             patch('redb.workers.download_s3_object', return_value="/tmp/downloaded.bin"):
            process_s3_file(
                "bucket", "key", "/tmp", False, "redb", "/tmp/log", 1, 1,
                decompile_modules=modules
            )

        mock_internal.assert_called_once()
        call_kwargs = mock_internal.call_args[1]
        assert call_kwargs["decompile_modules"] == modules

    @patch('redb.workers.process_binary_file')
    def test_process_zip_passes_decompile_modules(self, mock_process):
        """process_zip_file passes decompile_modules to process_binary_file."""
        from redb.workers import process_zip_file

        mock_process.return_value = (Mock(), "pebin")
        logger = Mock()
        modules = {"strings", "decompilation"}

        import tempfile
        import zipfile

        with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as tmp:
            tmp_path = tmp.name

        try:
            with zipfile.ZipFile(tmp_path, 'w') as zf:
                zf.writestr("test.bin", b"\x00" * 100)

            process_zip_file(
                tmp_path, False, "redb", logger, "all",
                decompile_modules=modules
            )

            mock_process.assert_called_once()
            call_kwargs = mock_process.call_args[1]
            assert call_kwargs["decompile_modules"] == modules
        finally:
            os.unlink(tmp_path)

    @patch('redb.workers.process_binary_file')
    def test_process_7zip_passes_decompile_modules(self, mock_process):
        """process_7zip_file passes decompile_modules to process_binary_file."""
        from redb.workers import process_7zip_file

        mock_process.return_value = (Mock(), "pebin")
        logger = Mock()
        modules = {"cfg", "llil"}

        with patch('redb.workers.py7zr') as mock_py7zr:
            mock_zf = Mock()
            mock_py7zr.SevenZipFile.return_value.__enter__ = Mock(return_value=mock_zf)
            mock_py7zr.SevenZipFile.return_value.__exit__ = Mock(return_value=False)

            def fake_extractall(path):
                dest = os.path.join(path, "test.bin")
                with open(dest, "wb") as f:
                    f.write(b"\x00" * 100)

            mock_zf.extractall = fake_extractall

            process_7zip_file(
                "/tmp/test.7z", False, "redb", logger, "all",
                decompile_modules=modules
            )

            if mock_process.called:
                call_kwargs = mock_process.call_args[1]
                assert call_kwargs["decompile_modules"] == modules

    def test_worker_unpacks_decompile_modules(self):
        """worker() correctly unpacks decompile_modules from args tuple."""
        from redb.workers import worker

        modules = {"strings", "cfg"}

        with patch('redb.workers.process_file') as mock_pf, \
             patch('redb.workers.signal'), \
             patch('redb.workers.gc'):
            mock_pf.return_value = (Mock(), "pebin")

            args = (
                "/tmp/test.bin",  # filepath
                True,             # decompile
                "redb",           # index_prefix
                "/tmp/log",       # log_file
                1,                # file_number
                10,               # total_files
                "all",            # selected_modules
                False,            # dry_run
                False,            # yara_scan
                False,            # with_yara
                False,            # force
                modules,          # decompile_modules
            )

            worker(args)

            mock_pf.assert_called_once()
            call_kwargs = mock_pf.call_args[1]
            assert call_kwargs["decompile_modules"] == modules


# ============================================================================
# BinaryNinjaDecompiler._module_selected() Tests
# ============================================================================

class TestModuleSelected:
    """Tests for BinaryNinjaDecompiler._module_selected() helper."""

    def _make_decompiler(self, decompile_modules):
        """Create a minimal BinaryNinjaDecompiler-like object for testing _module_selected."""

        class FakeDecompiler:
            def __init__(self, modules):
                self.decompile_modules = modules

            def _module_selected(self, module_name):
                return "all" in self.decompile_modules or module_name in self.decompile_modules

        return FakeDecompiler(decompile_modules)

    def test_all_selects_everything(self):
        d = self._make_decompiler({"all"})
        assert d._module_selected("strings") is True
        assert d._module_selected("decompilation") is True
        assert d._module_selected("disassembly") is True
        assert d._module_selected("cfg") is True
        assert d._module_selected("llil") is True

    def test_specific_module_selected(self):
        d = self._make_decompiler({"cfg", "strings"})
        assert d._module_selected("cfg") is True
        assert d._module_selected("strings") is True
        assert d._module_selected("decompilation") is False
        assert d._module_selected("disassembly") is False
        assert d._module_selected("llil") is False

    def test_empty_set_selects_nothing(self):
        d = self._make_decompiler(set())
        assert d._module_selected("strings") is False
        assert d._module_selected("decompilation") is False

    def test_single_module(self):
        d = self._make_decompiler({"llil"})
        assert d._module_selected("llil") is True
        assert d._module_selected("cfg") is False
        assert d._module_selected("strings") is False


# ============================================================================
# analyze_binary() Selective Execution Tests
# ============================================================================

class TestAnalyzeBinaryGating:
    """Tests that analyze_binary() gates extraction per decompile_modules.

    Uses a FakeDecompiler to test the gating logic without importing binaryninja.
    """

    def _make_analyze_binary_runner(self, decompile_modules):
        """Create a test harness that simulates the gating logic of analyze_binary()."""
        run_all = "all" in decompile_modules
        run_strings = run_all or "strings" in decompile_modules
        run_decompilation = run_all or "decompilation" in decompile_modules
        run_disassembly = run_all or "disassembly" in decompile_modules
        run_llil = run_all or "llil" in decompile_modules
        run_cfg = run_all or "cfg" in decompile_modules
        need_per_function = run_decompilation or run_disassembly or run_llil or run_cfg

        return {
            "run_strings": run_strings,
            "run_decompilation": run_decompilation,
            "run_disassembly": run_disassembly,
            "run_llil": run_llil,
            "run_cfg": run_cfg,
            "need_per_function": need_per_function,
        }

    def test_all_runs_everything(self):
        r = self._make_analyze_binary_runner({"all"})
        assert r["run_strings"] is True
        assert r["run_decompilation"] is True
        assert r["run_disassembly"] is True
        assert r["run_llil"] is True
        assert r["run_cfg"] is True
        assert r["need_per_function"] is True

    def test_strings_only_skips_per_function_loop(self):
        """Selecting only 'strings' should skip the per-function loop entirely."""
        r = self._make_analyze_binary_runner({"strings"})
        assert r["run_strings"] is True
        assert r["run_decompilation"] is False
        assert r["run_disassembly"] is False
        assert r["run_llil"] is False
        assert r["run_cfg"] is False
        assert r["need_per_function"] is False

    def test_cfg_only_runs_cfg_and_disassembly_in_loop(self):
        """Selecting only 'cfg' should run the per-function loop but skip decompilation/llil/strings."""
        r = self._make_analyze_binary_runner({"cfg"})
        assert r["run_strings"] is False
        assert r["run_decompilation"] is False
        assert r["run_disassembly"] is False  # Not selected (but backbone runs anyway in real code)
        assert r["run_llil"] is False
        assert r["run_cfg"] is True
        assert r["need_per_function"] is True

    def test_decompilation_only(self):
        r = self._make_analyze_binary_runner({"decompilation"})
        assert r["run_decompilation"] is True
        assert r["run_strings"] is False
        assert r["run_cfg"] is False
        assert r["run_llil"] is False
        assert r["need_per_function"] is True

    def test_multiple_modules(self):
        r = self._make_analyze_binary_runner({"cfg", "llil", "strings"})
        assert r["run_strings"] is True
        assert r["run_cfg"] is True
        assert r["run_llil"] is True
        assert r["run_decompilation"] is False
        assert r["run_disassembly"] is False
        assert r["need_per_function"] is True

    def test_disassembly_only(self):
        r = self._make_analyze_binary_runner({"disassembly"})
        assert r["run_disassembly"] is True
        assert r["run_decompilation"] is False
        assert r["run_strings"] is False
        assert r["run_cfg"] is False
        assert r["run_llil"] is False
        assert r["need_per_function"] is True


# ============================================================================
# prepare_export_data() Table Filtering Tests
# ============================================================================

class TestPrepareExportDataFiltering:
    """Tests that prepare_export_data() only includes tables for selected modules.

    Simulates the filtering logic from DecompileBinja.prepare_export_data()
    without importing binaryninja.
    """

    def _get_export_tables(self, decompile_modules):
        """Simulate the table selection logic from prepare_export_data()."""
        run_all = "all" in decompile_modules
        run_decompilation = run_all or "decompilation" in decompile_modules
        run_disassembly = run_all or "disassembly" in decompile_modules
        run_llil = run_all or "llil" in decompile_modules
        run_cfg = run_all or "cfg" in decompile_modules
        run_strings = run_all or "strings" in decompile_modules

        tables = set()
        if run_decompilation:
            tables.update(["decompiled_content", "decompiled_refs"])
        if run_disassembly:
            tables.update(["disassembled_content", "disassembled_refs", "function_similarity_metrics"])
        if run_llil:
            tables.update(["llil_content", "llil_refs"])
        if run_cfg:
            tables.update(["cfg_functions"])
        if run_strings:
            tables.update(["strings_raw"])
        # Errors always included if per-function loop ran
        if run_decompilation or run_disassembly or run_llil or run_cfg:
            tables.add("function_analysis_errors")

        return tables

    def test_all_includes_all_tables(self):
        tables = self._get_export_tables({"all"})
        assert "decompiled_content" in tables
        assert "decompiled_refs" in tables
        assert "disassembled_content" in tables
        assert "disassembled_refs" in tables
        assert "function_similarity_metrics" in tables
        assert "llil_content" in tables
        assert "llil_refs" in tables
        assert "cfg_functions" in tables
        assert "strings_raw" in tables
        assert "function_analysis_errors" in tables

    def test_strings_only_includes_only_strings(self):
        tables = self._get_export_tables({"strings"})
        assert tables == {"strings_raw"}

    def test_cfg_only_includes_cfg_and_errors(self):
        tables = self._get_export_tables({"cfg"})
        assert tables == {"cfg_functions", "function_analysis_errors"}

    def test_decompilation_only_tables(self):
        tables = self._get_export_tables({"decompilation"})
        assert tables == {"decompiled_content", "decompiled_refs", "function_analysis_errors"}

    def test_disassembly_only_tables(self):
        tables = self._get_export_tables({"disassembly"})
        assert tables == {
            "disassembled_content", "disassembled_refs",
            "function_similarity_metrics", "function_analysis_errors"
        }

    def test_llil_only_tables(self):
        tables = self._get_export_tables({"llil"})
        assert tables == {"llil_content", "llil_refs", "function_analysis_errors"}

    def test_combined_cfg_strings_tables(self):
        tables = self._get_export_tables({"cfg", "strings"})
        assert tables == {"cfg_functions", "strings_raw", "function_analysis_errors"}

    def test_no_errors_table_for_strings_only(self):
        """Error table should NOT be included when only strings is selected (no per-function loop)."""
        tables = self._get_export_tables({"strings"})
        assert "function_analysis_errors" not in tables


# ============================================================================
# DecompileBinja Parameter Acceptance Tests
# ============================================================================

class TestDecompileBinjaDecompileModules:
    """Tests that DecompileBinja accepts and stores decompile_modules.

    Since DecompileBinja imports binaryninja (which requires blake3 etc),
    we test the default/init logic in isolation.
    """

    def test_default_decompile_modules(self):
        """decompile_modules defaults to {'all'} when None."""
        modules = None
        result = modules or {"all"}
        assert result == {"all"}

    def test_explicit_decompile_modules(self):
        """Explicit decompile_modules is preserved."""
        modules = {"cfg", "llil"}
        result = modules or {"all"}
        assert result == {"cfg", "llil"}

    def test_empty_set_becomes_all(self):
        """Empty set is falsy and defaults to {'all'}."""
        modules = set()
        result = modules or {"all"}
        assert result == {"all"}


# ============================================================================
# Integration-Style: analyze_binary() Per-Function Gating (Logic Tests)
# ============================================================================

class TestAnalyzeBinaryPerFunctionGating:
    """Test that the per-function extraction gating produces correct results structures.

    These test the logic patterns from analyze_binary() using mock data,
    verifying that:
    - Unselected modules produce empty result lists
    - Selected modules produce populated result lists
    - Disassembly always runs when any per-function module is selected
    - Cross-linkage None handling works when counterparts are skipped
    """

    def _simulate_per_function_results(self, decompile_modules, num_functions=3):
        """Simulate the per-function extraction loop from analyze_binary().

        Returns results dict with the same structure as analyze_binary().
        """
        run_all = "all" in decompile_modules
        run_strings = run_all or "strings" in decompile_modules
        run_decompilation = run_all or "decompilation" in decompile_modules
        run_disassembly = run_all or "disassembly" in decompile_modules
        run_llil = run_all or "llil" in decompile_modules
        run_cfg = run_all or "cfg" in decompile_modules
        need_per_function = run_decompilation or run_disassembly or run_llil or run_cfg

        results = {
            "decompiled": [],
            "disassembled": [],
            "cfg": [],
            "llil": [],
            "errors": [],
            "strings": [],
        }

        if run_strings:
            results["strings"] = [{"string": f"str_{i}"} for i in range(5)]

        if not need_per_function:
            return results

        for i in range(num_functions):
            hlil_json = {"decompiled_function_hash": f"hlil_{i}"} if run_decompilation else None
            disass_json = {"disassembled_function_hash": f"disass_{i}", "disassembled_function_name": f"func_{i}", "disassembled_function_address": i * 100}  # always
            cfg_json = {"function_address": i * 100, "cyclomatic_complexity": 5} if run_cfg else None
            lowlevel_json = {"sha256_llil": f"llil_{i}", "tlsh_llil": "def", "minhash": [1, 2]} if run_llil else None

            # Simulate cross-linkage (same pattern as real code)
            if hlil_json and disass_json:
                hlil_json["disassembled_function_hash"] = disass_json["disassembled_function_hash"]
                disass_json["decompiled_function_hash"] = hlil_json["decompiled_function_hash"]
                results["decompiled"].append(hlil_json)
                if run_disassembly:
                    results["disassembled"].append(disass_json)
            elif hlil_json:
                hlil_json["disassembled_function_hash"] = None
                results["decompiled"].append(hlil_json)
            elif disass_json:
                disass_json["decompiled_function_hash"] = None
                if run_disassembly:
                    results["disassembled"].append(disass_json)

            if lowlevel_json and disass_json:
                lowlevel_json["disassembled_function_hash"] = disass_json["disassembled_function_hash"]
                disass_json["tlsh_llil"] = lowlevel_json.get("tlsh_llil")
                disass_json["minhash"] = lowlevel_json.get("minhash")
            elif lowlevel_json:
                lowlevel_json["disassembled_function_hash"] = None
            elif disass_json:
                disass_json["tlsh_llil"] = None
                disass_json["minhash"] = None

            if lowlevel_json:
                results["llil"].append(lowlevel_json)

            if cfg_json and disass_json:
                cfg_json["disassembled_function_hash"] = disass_json["disassembled_function_hash"]
                disass_json["cyclomatic_complexity"] = cfg_json.get("cyclomatic_complexity")
                results["cfg"].append(cfg_json)
            elif cfg_json:
                cfg_json["disassembled_function_hash"] = None
                results["cfg"].append(cfg_json)

            if disass_json and "cyclomatic_complexity" not in disass_json:
                disass_json["cyclomatic_complexity"] = None

        return results

    def test_all_populates_everything(self):
        r = self._simulate_per_function_results({"all"}, 3)
        assert len(r["decompiled"]) == 3
        assert len(r["disassembled"]) == 3
        assert len(r["cfg"]) == 3
        assert len(r["llil"]) == 3
        assert len(r["strings"]) == 5

    def test_strings_only_populates_strings(self):
        r = self._simulate_per_function_results({"strings"}, 3)
        assert len(r["strings"]) == 5
        assert len(r["decompiled"]) == 0
        assert len(r["disassembled"]) == 0
        assert len(r["cfg"]) == 0
        assert len(r["llil"]) == 0

    def test_cfg_only_populates_cfg_no_disassembly_export(self):
        """With only 'cfg' selected, cfg list is populated but disassembled is empty
        (disassembly runs internally but not exported)."""
        r = self._simulate_per_function_results({"cfg"}, 3)
        assert len(r["cfg"]) == 3
        assert len(r["disassembled"]) == 0  # Not selected for export
        assert len(r["decompiled"]) == 0
        assert len(r["llil"]) == 0
        assert len(r["strings"]) == 0

    def test_cfg_has_disassembled_function_hash_linkage(self):
        """CFG entries get disassembled_function_hash from backbone disassembly."""
        r = self._simulate_per_function_results({"cfg"}, 2)
        for cfg in r["cfg"]:
            assert cfg["disassembled_function_hash"] is not None
            assert cfg["disassembled_function_hash"].startswith("disass_")

    def test_decompilation_only_has_none_disassembly_linkage_when_no_disasm(self):
        """When only decompilation selected, disassembled still runs (backbone)
        so linkage is still available."""
        r = self._simulate_per_function_results({"decompilation"}, 2)
        assert len(r["decompiled"]) == 2
        # Disassembly runs as backbone, so linkage hash is present
        for d in r["decompiled"]:
            assert d["disassembled_function_hash"] is not None

    def test_disassembly_without_llil_has_none_fuzzy_hashes(self):
        """When LLIL is not selected, disassembly records have None fuzzy hashes."""
        r = self._simulate_per_function_results({"disassembly"}, 2)
        for d in r["disassembled"]:
            assert d["tlsh_llil"] is None
            assert d["minhash"] is None

    def test_disassembly_without_decompilation_has_none_decompiled_hash(self):
        """When decompilation is not selected, disassembly has None decompiled_function_hash."""
        r = self._simulate_per_function_results({"disassembly"}, 2)
        for d in r["disassembled"]:
            assert d["decompiled_function_hash"] is None

    def test_disassembly_without_cfg_has_none_cyclomatic(self):
        """When CFG is not selected, disassembly has None cyclomatic_complexity."""
        r = self._simulate_per_function_results({"disassembly"}, 2)
        for d in r["disassembled"]:
            assert d["cyclomatic_complexity"] is None

    def test_all_linkage_populated(self):
        """When all modules run, all cross-linkage fields are populated."""
        r = self._simulate_per_function_results({"all"}, 2)
        for d in r["disassembled"]:
            assert d["decompiled_function_hash"] is not None
            assert d["tlsh_llil"] is not None
            assert d["cyclomatic_complexity"] is not None
        for h in r["decompiled"]:
            assert h["disassembled_function_hash"] is not None
        for c in r["cfg"]:
            assert c["disassembled_function_hash"] is not None
        for ll in r["llil"]:
            assert ll["disassembled_function_hash"] is not None


# ============================================================================
# IOC Extraction Gating by decompile_modules
# ============================================================================

class TestIOCExtractionGating:
    """Tests that IOC extraction only runs when its data sources are present.

    IOCExtractorFromResults consumes in-memory analysis_results['strings']
    and analysis_results['decompiled']. It runs automatically (implicit
    dependency) when 'decompilation' or 'strings' modules produce data.
    It is NOT a standalone selectable module.
    """

    @staticmethod
    def _is_ioc_relevant(decompile_modules):
        """Replicate the gating logic from workers.py."""
        return (
            not decompile_modules
            or "all" in decompile_modules
            or "decompilation" in decompile_modules
            or "strings" in decompile_modules
        )

    def test_all_triggers_ioc(self):
        assert self._is_ioc_relevant({"all"}) is True

    def test_decompilation_triggers_ioc(self):
        assert self._is_ioc_relevant({"decompilation"}) is True

    def test_strings_triggers_ioc(self):
        assert self._is_ioc_relevant({"strings"}) is True

    def test_decompilation_and_strings_triggers_ioc(self):
        assert self._is_ioc_relevant({"decompilation", "strings"}) is True

    def test_cfg_only_skips_ioc(self):
        assert self._is_ioc_relevant({"cfg"}) is False

    def test_disassembly_only_skips_ioc(self):
        assert self._is_ioc_relevant({"disassembly"}) is False

    def test_llil_only_skips_ioc(self):
        assert self._is_ioc_relevant({"llil"}) is False

    def test_cfg_and_disassembly_skips_ioc(self):
        assert self._is_ioc_relevant({"cfg", "disassembly"}) is False

    def test_cfg_and_llil_skips_ioc(self):
        assert self._is_ioc_relevant({"cfg", "llil"}) is False

    def test_cfg_and_decompilation_triggers_ioc(self):
        """If decompilation is included alongside cfg, IOC should run."""
        assert self._is_ioc_relevant({"cfg", "decompilation"}) is True

    def test_disassembly_and_strings_triggers_ioc(self):
        """If strings is included alongside disassembly, IOC should run."""
        assert self._is_ioc_relevant({"disassembly", "strings"}) is True

    def test_none_modules_triggers_ioc(self):
        """When decompile_modules is None (default), IOC should run."""
        assert self._is_ioc_relevant(None) is True

    def test_empty_set_triggers_ioc(self):
        """When decompile_modules is empty set, IOC should run (falsy)."""
        assert self._is_ioc_relevant(set()) is True