Mainak Chatterjee

188 papers A* 5A 1B 36C 12Misc 8Journal 66Unranked 59
YearRankTypeTitle / Venue / Authors
2021 J jnl
Computing
Judhajit Sanyal, Tuhina Samanta, Mainak Chatterjee
2020 J jnl
ACM Trans. Sens. Networks
Mehrdad Salimitari, Shameek Bhattacharjee, Mainak Chatterjee, Yaser P. Fallah
2020 J jnl
Internet Things
Mehrdad Salimitari, Mainak Chatterjee, Yaser P. Fallah
2020 J jnl
Pervasive Mob. Comput.
Debashri Roy, Tathagata Mukherjee, Mainak Chatterjee, Eduardo Pasiliao
2020 J jnl
IEEE Trans. Cogn. Commun. Netw.
Debashri Roy, Tathagata Mukherjee, Mainak Chatterjee, Erik Blasch, Eduardo Pasiliao
2019 B conf
GLOBECOM
Mehrdad Salimitari, Mohsen Joneidi, Mainak Chatterjee
2019 J jnl
CoRR
Mehrdad Salimitari, Mohsen Joneidi, Mainak Chatterjee
2019 Misc conf
ICDCN
Swapnoneel Roy, Faustina J. Anto Morais, Mehrdad Salimitari, Mainak Chatterjee
2019 B conf
GLOBECOM
Debashri Roy, Tathagata Mukherjee, Mainak Chatterjee, Eduardo Pasiliao
2019 B conf
WCNC
Debashri Roy, Tathagata Mukherjee, Mainak Chatterjee, Eduardo Pasiliao
2019 J jnl
IEEE Commun. Mag.
Debashri Roy, Tathagata Mukherjee, Mainak Chatterjee
2019 J jnl
Comput. Commun.
Enas F. Khairullah, Mainak Chatterjee
2019 conf
DySPAN
Debashri Roy, Tathagata Mukherjee, Mainak Chatterjee, Eduardo Pasiliao
2019 C conf
ICMLA
Debashri Roy, Tathagata Mukherjee, Mainak Chatterjee, Eduardo Pasiliao
2019 J jnl
Future Gener. Comput. Syst.
Saptarshi Debroy, Priyanka Samanta, Amina Bashir, Mainak Chatterjee
2018 conf
ICC
Afraa Attiah, Mainak Chatterjee, Cliff C. Zou
2018 C conf
IPCCC
Debashri Roy, Tathagata Mukherjee, Mainak Chatterjee, Eduardo L. Pasiliao
2018 J jnl
CoRR
Mehrdad Salimitari, Mainak Chatterjee
2018 J jnl
Comput. Networks
Afraa Attiah, Muhammad Faisal Amjad, Mainak Chatterjee, Cliff C. Zou
2018 J jnl
CoRR
Mehrdad Salimitari, Shameek Bhattacharjee, Mainak Chatterjee
2018 J jnl
Pervasive Mob. Comput.
Enas F. Khairullah, Mainak Chatterjee
2018 J jnl
Online Soc. Networks Media
Anahita Davoudi, Mainak Chatterjee
2018 conf
ICDCN Workshops
Raj Mani Shukla, Shamik Sengupta, Mainak Chatterjee
2018 J jnl
Comput. Commun.
Debashri Roy, Mainak Chatterjee, Eduardo L. Pasiliao
2017 B conf
WCNC
Afraa Attiah, Mainak Chatterjee, Cliff C. Zou
2017 J jnl
Comput. Commun.
Osama Abbas Hussein Al Tameemi, Mainak Chatterjee
2017 conf
IEEE BigData
Anahita Davoudi, Mainak Chatterjee
2017 ed.
GAMENETS
Julian Cheng, Ekram Hossain, Haijun Zhang, Walid Saad, Mainak Chatterjee
2017 J jnl
IEEE Trans. Cogn. Commun. Netw.
Osama Abbas Hussein Al Tameemi, Ayad Al-Rumaithi, Mainak Chatterjee, Kevin A. Kwiat, Charles A. Kamhoua
2017 Misc conf
COMSNETS
Nirat Saini, Mainak Chatterjee
2017 B conf
MASS
Enas F. Khairullah, Mainak Chatterjee
2017 conf
DASC/PiCom/DataCom/CyberSciTech
Shameek Bhattacharjee, Mehrdad Salimitari, Mainak Chatterjee, Kevin A. Kwiat, Charles A. Kamhoua
2017 conf
CIC
Mehrdad Salimitari, Mainak Chatterjee, Murat Yuksel, Eduardo L. Pasiliao
2017 J jnl
IEEE Trans. Cogn. Commun. Netw.
Shameek Bhattacharjee, Saptarshi Debroy, Mainak Chatterjee
2017 C conf
WoWMoM
Enas F. Khairullah, Mainak Chatterjee, Swades De
2016 B conf
LCN
Osama Abbas Al Tameemi, Mainak Chatterjee
2016 conf
IEEE SENSORS
Mrinmoy Sen, Indrajit Banerjee, Mainak Chatterjee, Tuhina Samanta
2016 conf
ICTCS
Supantha Das, Indrajit Banerjee, Mainak Chatterjee, Tuhina Samanta
2016 B conf
GLOBECOM
Afraa Attiah, Muhammad Faisal Amjad, Mainak Chatterjee, Cliff C. Zou
2016 J jnl
Comput. Networks
Muhammad Faisal Amjad, Mainak Chatterjee, Cliff C. Zou
2016 Misc conf
ICNC
Osama Abbas Al Tameemi, Ayad Al-Rumaithi, Mainak Chatterjee, Kevin A. Kwiat, Charles A. Kamhoua
2016 J jnl
Wirel. Commun. Mob. Comput.
Muhammad Faisal Amjad, Mainak Chatterjee, Omar Nakhila, Cliff C. Zou
2016 conf
EuCNC
Enas F. Khairullah, Mainak Chatterjee
2016 conf
IEEE BigData
Anahita Davoudi, Mainak Chatterjee
2016 conf
CCNC
Anahita Davoudi, Mainak Chatterjee
2016 conf
VTC Fall
Enas F. Khairullah, Mainak Chatterjee
2015 conf
ISSNIP
Benazir Salma Jamin, Mainak Chatterjee, Tuhina Samanta
2015 B conf
IM
Shameek Bhattacharjee, Mainak Chatterjee, Kevin A. Kwiat, Charles A. Kamhoua
2015 Misc conf
ICNC
Osama Abbas Al Tameemi, Mainak Chatterjee, Kevin A. Kwiat, Charles A. Kamhoua
2015 conf
MILCOM
Osama Abbas Al Tameemi, Mainak Chatterjee, Kevin A. Kwiat, Charles A. Kamhoua
2015 J jnl
Pervasive Mob. Comput.
Jiannong Cao, Mainak Chatterjee
2015 conf
MILCOM
Shameek Bhattacharjee, Mainak Chatterjee, Kevin A. Kwiat, Charles A. Kamhoua
2015 conf
ICC
Osama Abbas Al Tameemi, Mainak Chatterjee, Kevin A. Kwiat, Charles A. Kamhoua
2015 conf
MILCOM
Anahita Davoudi, Mainak Chatterjee
2015 conf
Sarnoff Symposium
Anahita Davoudi, Mainak Chatterjee
2015 conf
iNIS
Prasun Seal, Mainak Chatterjee, Tuhina Samanta
2015 conf
ICACCI
Navin Kumar H. S., Mainak Chatterjee
2015 conf
WCNC Workshops
Mrinmoy Sen, Indrajit Banerjee, Mainak Chatterjee, Tuhina Samanta
2015 J jnl
IEEE Trans. Cogn. Commun. Netw.
Swastik Brahma, Mainak Chatterjee
2015 J jnl
IEEE Trans. Cogn. Commun. Netw.
Saptarshi Debroy, Shameek Bhattacharjee, Mainak Chatterjee
2015 conf
ICACCI
Shobbana Santhoshini J. R., Mainak Chatterjee
2015 J jnl
Wirel. Networks
Osama Abbas Al Tameemi, Mainak Chatterjee, Kevin A. Kwiat
2014 J jnl
Comput. Networks
Wenjing Wang, Mainak Chatterjee, Kevin A. Kwiat, Qing Li
2014 conf
GlobalSIP
Muhammad Faisal Amjad, Mainak Chatterjee, Omar Nakhila, Cliff C. Zou
2014 B conf
PIMRC
Enas F. Khairullah, Mainak Chatterjee
2014 J jnl
IEEE Trans. Mob. Comput.
Saptarshi Debroy, Swades De, Mainak Chatterjee
2014 Misc ed.
ICDCN
Mainak Chatterjee, Jiannong Cao, Kishore Kothapalli, Sergio Rajsbaum
2014 J jnl
IEEE/ACM Trans. Netw.
Mukundan Venkataraman, Mainak Chatterjee
2014 conf
MILCOM
Muhammad Faisal Amjad, Mainak Chatterjee, Cliff C. Zou
2014 J jnl
IEEE Wirel. Commun.
Ying-Dar Lin, Yu-Ching Hsu, Mainak Chatterjee, Thomas Kunz
2014 B conf
PIMRC
Osama Abbas Al Tameemi, Mainak Chatterjee, Ayad Al-Rumaithi
2014 conf
DySPAN
Osama Abbas Al Tameemi, Mainak Chatterjee
2014 C conf
ISDA
Supantha Das, Indrajit Banerjee, Mainak Chatterjee, Tuhina Samanta
2014 B conf
PIMRC
Saptarshi Debroy, Mainak Chatterjee
2014 B conf
PIMRC
Shameek Bhattacharjee, Mainak Chatterjee
2014 conf
WOCC
Osama Abbas Al Tameemi, Mainak Chatterjee, Kevin A. Kwiat
2013 J jnl
Pervasive Mob. Comput.
Wenjing Wang, Shameek Bhattacharjee, Mainak Chatterjee, Kevin A. Kwiat
2013 B conf
GLOBECOM
Saptarshi Debroy, Swades De, Mainak Chatterjee
2013 conf
ICC
Saptarshi Debroy, Mohammad Zubair Ahmad, Mukundan Iyengar, Mainak Chatterjee
2013 conf
LCN Workshops
Afraa Attiah, Mustafa Ilhan Akbas, Mainak Chatterjee, Damla Turgut
2013 conf
ICOIN
Sabari Somasekharan Nair, Sebastian Schellenberg, Jochen Seitz, Mainak Chatterjee
2013 conf
ICC
Swastik Brahma, Mainak Chatterjee
2013 B conf
GLOBECOM
Enas F. Khairullah, Mainak Chatterjee, Kevin A. Kwiat
2013 J jnl
Pervasive Mob. Comput.
Shamik Sengupta, Swastik Brahma, Mainak Chatterjee, Sai Shankar Nandagopalan
2013 conf
ICC
Shameek Bhattacharjee, Saptarshi Debroy, Mainak Chatterjee, Kevin A. Kwiat
2013 J jnl
Comput. Commun.
Shameek Bhattacharjee, Shamik Sengupta, Mainak Chatterjee
2012 A* conf
INFOCOM
Swastik Brahma, Mainak Chatterjee
2012 B conf
WCNC
Saptarshi Debroy, Shameek Bhattacharjee, Mainak Chatterjee, Kevin A. Kwiat
2012 J jnl
Comput. Commun.
Mukundan Venkataraman, Shamik Sengupta, Mainak Chatterjee, Raja Neogi
2012 conf
ICC
Swastik Brahma, Pramod K. Varshney, Mainak Chatterjee, Kevin A. Kwiat
2012 J jnl
IEEE/ACM Trans. Netw.
Mukundan Venkataraman, Mainak Chatterjee
2012 J jnl
Pervasive Mob. Comput.
Mainak Chatterjee, Andrea Passarella
2012 B conf
WCNC
Swastik Brahma, Mainak Chatterjee
2012 J jnl
Comput. Commun.
Swastik Brahma, Mainak Chatterjee, Kevin A. Kwiat, Pramod K. Varshney
2011 J jnl
Ad Hoc Networks
Mukundan Venkataraman, Mainak Chatterjee, Kevin A. Kwiat
2011 C conf
WOWMOM
Wenjing Wang, Mainak Chatterjee, Kevin A. Kwiat
2011 J jnl
IEEE Trans. Commun.
Wenjing Wang, Mainak Chatterjee, Kevin A. Kwiat
2011 A conf
MMSys
Mukundan Venkataraman, Mainak Chatterjee
2011 J jnl
IEEE Netw.
Mukundan Venkataraman, Mainak Chatterjee
2011 C conf
WOWMOM
Mainak Chatterjee, Andrea Passarella
2011 B conf
PIMRC
Saptarshi Debroy, Shameek Bhattacharjee, Mainak Chatterjee
2011 C conf
SIN
Shameek Bhattacharjee, Saptarshi Debroy, Mainak Chatterjee, Kevin A. Kwiat
2011 B conf
PIMRC
Shameek Bhattacharjee, Saptarshi Debroy, Mainak Chatterjee
2010 B conf
GLOBECOM
Swastik Brahma, Mainak Chatterjee
2010 J jnl
IEEE Trans. Computers
Shamik Sengupta, Mainak Chatterjee, Kevin A. Kwiat
2010 A* conf
ACM Multimedia
Mainak Chatterjee, Samrat Ganguly
2010 conf
ICC
Wenjing Wang, Mainak Chatterjee, Kevin A. Kwiat
2010 B conf
GLOBECOM
Mukundan Venkataraman, Mainak Chatterjee
2010 conf
PerCom Workshops
Swastik Brahma, Mainak Chatterjee, Kevin A. Kwiat
2010 conf
CCNC
Saptarshi Debroy, Mainak Chatterjee
2009 conf
CCNC
Mukundan Venkataraman, Shamik Sengupta, Mainak Chatterjee, Raja Neogi
2009 J jnl
IEEE/ACM Trans. Netw.
Shamik Sengupta, Mainak Chatterjee
2009 conf
GAMENETS
Wenjing Wang, Mainak Chatterjee, Kevin A. Kwiat
2009 B conf
SECON
Wenjing Wang, Mainak Chatterjee, Kevin A. Kwiat
2009 J jnl
Comput. J.
Daniel Lacks, Mainak Chatterjee, Taskin Koçak
2009 J jnl
Phys. Commun.
Shamik Sengupta, Santhanakrishnan Anand, Mainak Chatterjee, Rajarathnam Chandramouli
2009 B conf
WCNC
Shamik Sengupta, Mainak Chatterjee, Kevin A. Kwiat
2009 B conf
GLOBECOM
Mukundan Venkataraman, Mainak Chatterjee, Siddhartha Chattopadhyay
2009 C conf
WOWMOM
Swastik Brahma, Mainak Chatterjee
2009 B conf
GLOBECOM
Swastik Brahma, Mainak Chatterjee
2009 J jnl
IEEE Trans. Veh. Technol.
Wenjing Wang, Fei Xie, Mainak Chatterjee
2009 J jnl
Pervasive Mob. Comput.
Mainak Chatterjee
2009 B conf
WCNC
Mukundan Venkataraman, Mainak Chatterjee, Kevin A. Kwiat
2009 J jnl
Comput. Commun.
Wenjing Wang, Mainak Chatterjee, Kevin A. Kwiat
2008 B conf
GLOBECOM
Shamik Sengupta, Rajarathnam Chandramouli, Swastik Brahma, Mainak Chatterjee
2008 J jnl
Mob. Networks Appl.
Shamik Sengupta, Mainak Chatterjee
2008 Misc ed.
ICDCN
Shrisha Rao, Mainak Chatterjee, Prasad Jayanti, C. Siva Ram Murthy, Sanjoy Kumar Saha
2008 B conf
MASS
Wenjing Wang, Mainak Chatterjee, Kevin A. Kwiat
2008 J jnl
IEEE Trans. Computers
Shamik Sengupta, Mainak Chatterjee, Samrat Ganguly
2008 conf
ICNS
Mukundan Venkataraman, Shamik Sengupta, Mainak Chatterjee, Raja Neogi
2008 J jnl
Phys. Commun.
Wenjing Wang, Shamik Sengupta, Mainak Chatterjee
2007 C conf
WOWMOM
Wenjing Wang, Mainak Chatterjee, Kevin A. Kwiat
2007 C conf
WOWMOM
Kevin Schneider, Damla Turgut, Mainak Chatterjee
2007 J jnl
IEEE Trans. Computers
Sourav Pal, Sumantra R. Kundu, Mainak Chatterjee, Sajal K. Das
2007 conf
ISWCS
Wenjing Wang, Mainak Chatterjee, Kevin A. Kwiat
2007 conf
ICC
Shamik Sengupta, Swastik Brahma, Mainak Chatterjee, Sai Shankar N.
2007 J jnl
IEEE Wirel. Commun.
Mainak Chatterjee, Shamik Sengupta, Samrat Ganguly
2007 conf
CrownCom
Shamik Sengupta, Mohammad Zubair Ahmad, Mainak Chatterjee
2007 conf
ICC
Wenjing Wang, Shamik Sengupta, Mainak Chatterjee
2007 J jnl
IEEE Trans. Mob. Comput.
Mainak Chatterjee, Haitao Lin, Sajal K. Das
2007 conf
CrownCom
Shamik Sengupta, Mainak Chatterjee
2007 conf
VTC Fall
Wenjing Wang, Fei Xie, Mainak Chatterjee
2006 J jnl
IEEE Trans. Computers
Jaideep Sarkar, Shamik Sengupta, Mainak Chatterjee, Samrat Ganguly
2006 J jnl
J. Commun.
Swarup Mandal, Debashis Saha, Mainak Chatterjee
2006 conf
ICC
Shamik Sengupta, Mainak Chatterjee, Samrat Ganguly, Rauf Izmailov
2006 J jnl
IEEE Trans. Computers
Samrat Ganguly, Mainak Chatterjee, Rauf Izmailov
2006 conf
CCNC
Swarup Mandal, Debashis Saha, Mainak Chatterjee
2006 B conf
LCN
Wenjing Wang, Fei Xie, Mainak Chatterjee
2006 Misc conf
ICDCN
Shamik Sengupta, Mainak Chatterjee
2006 J jnl
Wirel. Networks
Haitao Lin, Mainak Chatterjee, Sajal K. Das
2005 conf
Wireless Information Systems
Ratan K. Guha, Mainak Chatterjee, Jaideep Sarkar
2005 J jnl
ACM SIGMOBILE Mob. Comput. Commun. Rev.
Sourav Pal, Mainak Chatterjee, Sajal K. Das
2005 J jnl
IEEE Trans. Mob. Comput.
Haitao Lin, Mainak Chatterjee, Sajal K. Das, Kalyan Basu
2005 C conf
WOWMOM
Shamik Sengupta, Mainak Chatterjee, Samrat Ganguly, Rauf Izmailov
2005 J jnl
IEEE Trans. Computers
Mainak Chatterjee, Giridhar D. Mandyam, Sajal K. Das
2005 conf
IWDC
Chun Huang, Mainak Chatterjee, Wei Cui, Ratan K. Guha
2005 J jnl
Mob. Networks Appl.
Mainak Chatterjee, Haitao Lin, Sajal K. Das
2005 B conf
GLOBECOM
Anand P. Santhanam, Balaji C. Neyveli, Mainak Chatterjee
2005 conf
ICC
Sourav Pal, Sajal K. Das, Mainak Chatterjee
2005 J jnl
Wirel. Networks
Mainak Chatterjee, Sajal K. Das
2005 B conf
WCNC
Shamik Sengupta, Mainak Chatterjee, Samrat Ganguly, Rauf Izmailov
2004 B conf
WCNC
Radhey Shah, Mainak Chatterjee
2004 J jnl
IEEE Netw.
Sajal K. Das, Haitao Lin, Mainak Chatterjee
2004 J jnl
IEEE J. Sel. Areas Commun.
Swades De, Chunming Qiao, Dimitrios A. Pados, Mainak Chatterjee, Sumesh J. Philip
2004 conf
HSNMC
Sourav Pal, Mainak Chatterjee, Sajal K. Das
2004 conf
IWDC
Shamik Sengupta, Mainak Chatterjee
2004 conf
HSNMC
Kiran Anna, Abhishek Karnik, Ratan K. Guha, Mainak Chatterjee
2004 conf
ICC
Mainak Chatterjee, Samrat Ganguly, Jaideep Sarkar
2004 B conf
WCNC
Sourav Pal, Mainak Chatterjee, Sajal K. Das
2004 B conf
PIMRC
Samrat Ganguly, Nan Tu, Mainak Chatterjee, Rauf Izmailov
2004 A* conf
INFOCOM
Swades De, Dimitrios A. Pados, Chunming Qiao, Mainak Chatterjee
2003 conf
ICC
Mainak Chatterjee, Haitao Lin, Sajal K. Das, Kalyan Basu
2003 A* conf
MobiCom
Haitao Lin, Mainak Chatterjee, Sajal K. Das, Kalyan Basu
2002 C conf
WOWMOM
Mainak Chatterjee, Sajal K. Das, Kalyan Basu
2002 B conf
PIMRC
Mainak Chatterjee, Giridhar D. Mandyam, Sajal K. Das
2002 A* conf
INFOCOM
Mainak Chatterjee, Sajal K. Das
2002 J jnl
J. High Speed Networks
Mainak Chatterjee, Sajal K. Das
2002 conf
VTC Spring
Mainak Chatterjee, Sajal K. Das, Giridhar D. Mandyam
2002 J jnl
Clust. Comput.
Mainak Chatterjee, Sajal K. Das, Damla Turgut
2001 conf
ICC
Sajal K. Das, Mainak Chatterjee, Naveen K. Kakani
2001 conf
ICN (1)
Mainak Chatterjee, Sajal K. Das, Giridhar D. Mandyam
2001 J jnl
J. Interconnect. Networks
Mainak Chatterjee, Sajal K. Das
2000 Misc conf
HiPC
Mainak Chatterjee, Sajal K. Das, Damla Turgut
2000 conf
ICON
Mainak Chatterjee, Sajal K. Das
2000 B conf
GLOBECOM
Mainak Chatterjee, Sajal K. Das, Damla Turgut
1999 B conf
WCNC
Sajal K. Das, Mainak Chatterjee, Naveen K. Kakani
1997 J jnl
Comput. Commun. Rev.
P. Krishna, Nitin H. Vaidya, Mainak Chatterjee, Dhiraj K. Pradhan
1995 conf
Symposium on Mobile and Location-Independent Computing
P. Krishna, Mainak Chatterjee, Nitin H. Vaidya, Dhiraj K. Pradhan
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