Xiangwang Hou

90 papers B 13Journal 66Unranked 11
YearRankTypeTitle / Venue / Authors
2026 J jnl
IEEE Trans. Cogn. Commun. Netw.
Xiangyun Tang, Ruiqi Peng, Hao Li, Tao Zhang, Xiangwang Hou, Xiangzhi Liu
2026 J jnl
IEEE Trans. Cogn. Commun. Netw.
Houze Feng, Jingjing Wang, Xiangwang Hou, Xin Zhang, Zhengyang Zhang, Zhu Han
2026 J jnl
IEEE Trans. Netw. Sci. Eng.
Ziqi Chen, Jun Du, Chunxiao Jiang, Xiangwang Hou, Zhu Han, H. Vincent Poor
2026 J jnl
IEEE Commun. Surv. Tutorials
Xiangwang Hou, Jingjing Wang, Jun Du, Chunxiao Jiang, Yong Ren
2026 J jnl
IEEE Trans. Veh. Technol.
Tianyun Ding, Xiangwang Hou, Zekai Zhang, Jingjing Wang, Jun Du, Chunxiao Jiang, Dusit Niyato
2026 J jnl
CoRR
Xu Guo, Fulong Ye, Qichao Sun, Liyang Chen, Bingchuan Li, Pengze Zhang, Jiawei Liu, Songtao Zhao, Qian He, Xiangwang Hou
2026 J jnl
CoRR
Xu Guo, Fulong Ye, Xinghui Li, Pengqi Tu, Pengze Zhang, Qichao Sun, Songtao Zhao, Xiangwang Hou, Qian He
2026 J jnl
IEEE Trans. Netw. Sci. Eng.
Ye Wang, Jingjing Wang, Jianrui Chen, Xiangwang Hou, Ziyang Wang, Chunxiao Jiang
2026 J jnl
IEEE Trans. Wirel. Commun.
Luxing Zhang, Jun Du, Chunxiao Jiang, Xiangwang Hou, Yong Ren, Zhu Han
2026 J jnl
IEEE Trans. Mob. Comput.
Yi Xia, Ronghua Zhang, Xiangwang Hou, Xin Xu, Jingjing Wang, Chunxiao Jiang, Dusit Niyato
2026 J jnl
IEEE Trans. Cogn. Commun. Netw.
Zekai Zhang, Shihong Li, Xiangwang Hou, Zonglin Li, Zhanyuan Xie, Yong Ren
2026 J jnl
IEEE Trans. Mob. Comput.
Guanwen Xie, Jingzehua Xu, Ziqi Zhang, Xiangwang Hou, Dongfang Ma, Shuai Zhang, Yong Ren, Dusit Niyato
2026 J jnl
IEEE Trans. Mob. Comput.
Xiangwang Hou, Jingjing Wang, Jun Du, Chunxiao Jiang, Yong Ren, Dusit Niyato
2026 J jnl
IEEE Wirel. Commun.
Jiahui Li, Geng Sun, Xiaoyu Sun, Fang Mei, Jingjing Wang, Xiangwang Hou, Daxin Tian, Victor C. M. Leung
2026 J jnl
IEEE Trans. Mob. Comput.
Zhang Liu, Hongyang Du, Xiangwang Hou, Lianfen Huang, Seyyedali Hosseinalipour, Dusit Niyato, Khaled Ben Letaief
2025 conf
ICC
Xu Guo, Xiangwang Hou, Minrui Xu, Jianrui Chen, Jingjing Wang, Jun Du, Yong Ren
2025 J jnl
CoRR
Xu Guo, Xiangwang Hou, Minrui Xu, Jianrui Chen, Jingjing Wang, Jun Du, Yong Ren
2025 J jnl
IEEE Trans. Mob. Comput.
Xiangwang Hou, Tianyu Xing, Jingjing Wang, Jun Du, Chunxiao Jiang, Yong Ren, Dusit Niyato
2025 J jnl
CoRR
Mingyi Luo, Ruichen Zhang, Xiangwang Hou, Jun Du, Chunxiao Jiang, Yong Ren, Dusit Niyato, Shiwen Mao
2025 J jnl
IEEE Trans. Instrum. Meas.
Hao Liu, Yue Sun, Dongmei Chen, Tao Huang, Xiangwang Hou, Yong Ren, Lan Ding, Xiaodong Liu
2025 B conf
WCNC
Xiaolong Xu, Ying Ju, Zhenghao Li, Xiangwang Hou, Lei Liu, Shahid Mumtaz, Celimuge Wu
2025 J jnl
IEEE Trans. Signal Process.
Feiran You, Hongyang Du, Xiangwang Hou, Yong Ren, Kaibin Huang
2025 J jnl
CoRR
Feiran You, Hongyang Du, Xiangwang Hou, Yong Ren, Kaibin Huang
2025 J jnl
IEEE Trans. Consumer Electron.
Xingling Shao, Jun Du, Yi Xia, Zekai Zhang, Xiangwang Hou, Mérouane Debbah
2025 B conf
GLOBECOM
Wei Men, Longfei Zhao, Yong Liang Guan, Xiangwang Hou, Yong Ren, Dusit Niyato
2025 J jnl
CoRR
Wei Men, Longfei Zhao, Yong Liang Guan, Xiangwang Hou, Yong Ren, Dusit Niyato
2025 J jnl
CoRR
Xiangwang Hou, Jingjing Wang, Fangming Guan, Jun Du, Chunxiao Jiang, Yong Ren
2025 J jnl
IEEE J. Sel. Areas Commun.
Xiangwang Hou, Jingjing Wang, Fangming Guan, Jun Du, Chunxiao Jiang, Yong Ren
2025 conf
ICC
Fangming Guan, Xiangwang Hou, Xianghe Wang, Jingjing Wang, Jun Du, Yong Ren
2025 conf
ICC
Xiangwang Hou, Jingjing Wang, Jiacheng Wang, Jun Du, Zekai Zhang, Chunxiao Jiang, Yong Ren
2025 J jnl
IEEE Internet Things J.
Zezhao Meng, Zhi Li, Xiangwang Hou, Minrui Xu, Yi Xia, Zekai Zhang, Shaoyang Song
2025 J jnl
IEEE Trans. Cogn. Commun. Netw.
Pengfei Ren, Jingjing Wang, Xiangwang Hou, Jianrui Chen, Chunxiao Jiang
2025 J jnl
IEEE Netw.
Pengfei Ren, Jingjing Wang, Jianrui Chen, Xiangwang Hou, Haohua Du, Chunxiao Jiang
2025 J jnl
CoRR
Xiangwang Hou, Jingjing Wang, Jun Du, Chunxiao Jiang, Yong Ren, Dusit Niyato
2025 conf
VTC2025-Spring
Pengfei Ren, Jingjing Wang, Zhiwei Wang, Xiangwang Hou, Xin Zhang, Chunxiao Jiang
2025 J jnl
IEEE Trans. Netw. Serv. Manag.
Yibo Zhang, Xiangwang Hou, Guoyu Du, Qi Li, Mian Ahmad Jan, Alireza Jolfaei, Muhammad Usman
2025 conf
ICC
Luxing Zhang, Xiangwang Hou, Jingjing Wang, Jun Du, Hongyang Du, Yong Ren
2025 J jnl
IEEE Trans. Commun.
Wei Men, Jingjing Wang, Bowen Dong, Xiangwang Hou, Chunxiao Jiang, Yong Ren
2025 J jnl
IEEE Trans. Consumer Electron.
Tianyu Xing, Jintao Zhang, Yi Xia, Xiangwang Hou, Zekai Zhang, Yong Ren
2025 J jnl
CoRR
Jiahui Li, Geng Sun, Xiaoyu Sun, Fang Mei, Jingjing Wang, Xiangwang Hou, Daxin Tian, Victor C. M. Leung
2025 J jnl
IEEE Trans. Consumer Electron.
Xianghe Wang, Shaoyang Song, Zekai Zhang, Xiangwang Hou, Zhiying Li, Tianyu Xing, Xiao-Ping Zhang
2025 J jnl
CoRR
Xiangwang Hou, Jingjing Wang, Zekai Zhang, Jiacheng Wang, Lei Liu, Yong Ren
2025 B conf
WCNC
Houze Feng, Jingjing Wang, Jianrui Chen, Xiangwang Hou, Jiacheng Wang, Geng Sun, Dusit Niyato
2025 J jnl
IEEE Commun. Surv. Tutorials
Ziyuan Wang, Jun Du, Xiangwang Hou, Jingjing Wang, Chunxiao Jiang, Xiao-Ping Zhang, Yong Ren
2025 B conf
GLOBECOM
Luxing Zhang, Jun Du, Xiangwang Hou, Wei Men, Minrui Xu, Yong Ren
2025 J jnl
IEEE Internet Things J.
Jingzehua Xu, Guanwen Xie, Zekai Zhang, Xiangwang Hou, Shuai Zhang, Yong Ren, Dusit Niyato
2025 J jnl
IEEE Trans. Wirel. Commun.
Haonan Si, Xiangwang Hou, Jingjing Wang, Gordon Owusu Boateng, Zekai Zhang, Xiansheng Guo, Dusit Niyato
2024 J jnl
IEEE Internet Things J.
Junhuai Li, Kan Wang, Xiangwang Hou, Dapeng Lan, Yunwen Wu, Huaijun Wang, Lei Liu, Shahid Mumtaz
2024 J jnl
IEEE Trans. Netw. Sci. Eng.
Jingjing Wang, Zihan Jiao, Jianrui Chen, Xiangwang Hou, Tingting Yang, Dapeng Lan
2024 J jnl
IEEE Internet Things J.
Ziheng Tong, Jingjing Wang, Xiangwang Hou, Jianrui Chen, Zihan Jiao, Jianwei Liu
2024 J jnl
IEEE J. Biomed. Health Informatics
Ziqi Chen, Jun Du, Xiangwang Hou, Keping Yu, Jintao Wang, Zhu Han
2024 B conf
GLOBECOM
Hualei Zhang, Jun Du, Xiangwang Hou, Chunxiao Jiang, Jintao Wang, Dusit Niyato
2024 B conf
GLOBECOM
Wei Men, Jun Du, Jintao Wang, Xiangwang Hou, Yong Ren, Dusit Niyato
2024 J jnl
IEEE Trans. Geosci. Remote. Sens.
Luxing Zhang, Huotao Gao, Jun Du, Xiangwang Hou, Wei Men, Yong Ren
2024 J jnl
IEEE J. Sel. Areas Commun.
Xiangwang Hou, Jingjing Wang, Chunxiao Jiang, Zezhao Meng, Jianrui Chen, Yong Ren
2024 J jnl
CoRR
Jingzehua Xu, Guanwen Xie, Ziqi Zhang, Xiangwang Hou, Dongfang Ma, Shuai Zhang, Yong Ren, Dusit Niyato
2024 J jnl
IEEE Trans. Veh. Technol.
Yibo Zhang, Xiangwang Hou, Hongyang Du, Lanjie Zhang, Jun Du, Wei Men
2024 J jnl
IEEE J. Sel. Areas Commun.
Sun Mao, Lei Liu, Xiangwang Hou, Mohammed Atiquzzaman, Kun Yang
2024 J jnl
CoRR
Zhang Liu, Hongyang Du, Xiangwang Hou, Lianfen Huang, Seyyedali Hosseinalipour, Dusit Niyato, Khaled Ben Letaief
2024 J jnl
IEEE Trans. Wirel. Commun.
Ziheng Tong, Jingjing Wang, Xiangwang Hou, Chunxiao Jiang, Jianwei Liu
2024 B conf
GLOBECOM
Xianghe Wang, Xiangwang Hou, Fangming Guan, Jun Du, Jingjing Wang, Yong Ren
2023 J jnl
IEEE Trans. Veh. Technol.
Yingkun Wen, Lei Liu, Junhuai Li, Xiangwang Hou, Ning Zhang, Mianxiong Dong, Mohammed Atiquzzaman, Kan Wang, Yan Huo
2023 J jnl
Sensors
Zekai Zhang, Shaoyang Song, Jingzehua Xu, Ziyuan Wang, Xiangwang Hou, Ming Zeng, Wei Men, Yong Ren
2023 J jnl
IEEE J. Sel. Areas Commun.
Xiangwang Hou, Jingjing Wang, Tong Bai, Yansha Deng, Yong Ren, Lajos Hanzo
2023 J jnl
Veh. Commun.
Yi Gong, Qingyu Li, Lei Liu, Fanke Meng, Xiangwang Hou, Zhan Xu
2023 J jnl
Sensors
Ziyuan Wang, Jingzehua Xu, Yuanzhe Feng, Yijing Wang, Guanwen Xie, Xiangwang Hou, Wei Men, Yong Ren
2023 J jnl
IEEE J. Sel. Areas Commun.
Jie Feng, Lei Liu, Xiangwang Hou, Qingqi Pei, Celimuge Wu
2023 B conf
GLOBECOM
Liying Fu, Wenting Wei, Xueyu Lu, Celimuge Wu, Xiangwang Hou, Lei Lin, Chen Chen
2023 conf
ICCC Workshops
Xianghe Wang, Zezhao Meng, Xiangwang Hou, Jun Du, Ruiqi Liu, Yong Ren
2023 J jnl
IEEE Trans. Wirel. Commun.
Xiangwang Hou, Jingjing Wang, Chunxiao Jiang, Xudong Zhang, Yong Ren, Mérouane Debbah
2022 J jnl
IEEE Netw.
Xiangwang Hou, Jingjing Wang, Zhengru Fang, Yong Ren, Kwang-Cheng Chen, Lajos Hanzo
2022 J jnl
Sensors
Zezhao Meng, Zhi Li, Xiangwang Hou, Jun Du, Jianrui Chen, Wei Wei
2022 J jnl
CoRR
Xiangwang Hou, Jingjing Wang, Tong Bai, Yansha Deng, Yong Ren, Lajos Hanzo
2022 J jnl
CoRR
Xiangwang Hou, Zhiyuan Ren, Wenchi Cheng, Chen Chen, Hailin Zhang
2022 B conf
IWCMC
Mingyue Zhao, Xing Zhang, Zezhao Meng, Xiangwang Hou
2022 J jnl
IEEE Internet Things J.
Zhengru Fang, Jingjing Wang, Jun Du, Xiangwang Hou, Yong Ren, Zhu Han
2021 B conf
IWCMC
Sanghai Guan, Jingjing Wang, Chunxiao Jiang, Xiangwang Hou, Zhengru Fang, Yong Ren
2021 conf
ICC
Zhengru Fang, Jingjing Wang, Chunxiao Jiang, Jun Du, Xiangwang Hou, Yong Ren
2021 J jnl
IEEE Syst. J.
Ying Wang, Zhendong Li, Yuanbin Chen, Man Liu, Xinpeng Lyu, Xiangwang Hou, Jingjing Wang
2021 J jnl
IEEE Netw.
Xiangwang Hou, Jingjing Wang, Zhengru Fang, Xin Zhang, Shenghui Song, Xudong Zhang, Yong Ren
2020 J jnl
IEEE Access
Xiangwang Hou, Zhiyuan Ren, Jingjing Wang, Shuya Zheng, Wenchi Cheng, Hailin Zhang
2020 J jnl
IEEE Trans. Ind. Informatics
Jingjing Wang, Chunxiao Jiang, Kai Zhang, Xiangwang Hou, Yong Ren, Yi Qian
2020 conf
INFOCOM Workshops
Xiangwang Hou, Zhiyuan Ren, Jingjing Wang, Shuya Zheng, Hailin Zhang
2020 B conf
GLOBECOM
Shuya Zheng, Zhiyuan Ren, Xiangwang Hou, Hailin Zhang
2020 conf
ICC Workshops
Nanxin Wang, Yifei Xin, Jingheng Zheng, Jingjing Wang, Xiao Liu, Xiangwang Hou, Yuanwei Liu
2020 J jnl
IEEE Internet Things J.
Xiangwang Hou, Zhiyuan Ren, Jingjing Wang, Wenchi Cheng, Yong Ren, Kwang-Cheng Chen, Hailin Zhang
2019 conf
ICC
Xiangwang Hou, Zhiyuan Ren, Wenchi Cheng, Chen Chen, Hailin Zhang
2019 B conf
WCNC
Xiangwang Hou, Zhiyuan Ren, Kun Yang, Chen Chen, Hailin Zhang, Yao Xiao
2018 conf
SmartIoT
Yiming Wang, Zhiyuan Ren, Hailin Zhang, Xiangwang Hou, Yao Xiao
2018 B conf
WCNC
Chenhua Shi, Zhiyuan Ren, Kun Yang, Chen Chen, Hailin Zhang, Yao Xiao, Xiangwang Hou
tests/unit/test_apk_strings_ioc.py
← Index tests/unit/test_apk_strings_ioc.py python
"""Tests for APK string extraction and IOC extraction integration.

Covers:
- APKCodeAnalyzer._extract_strings() — string extraction from DEX objects
- APKCodeAnalyzer._string_entropy() — Shannon entropy computation
- DecompileAPK strings export table schema
- IOCExtractorFromResults with APK-format analysis_results
- Worker IOC wiring for APK
"""
import os
import tempfile
from dataclasses import asdict
from unittest.mock import MagicMock, patch

import pytest

from redb.extractors.ioc_extractor.ioc_extractor import IOCExtractorFromResults
from redb.extractors.ioc_extractor.standalone_ioc_extractor import (
    IOCType,
    SourceType,
)

pytestmark = [pytest.mark.unit, pytest.mark.apk]


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

def _make_analyzer(min_instructions=1):
    """Create an APKCodeAnalyzer with mocked dependencies."""
    with patch(
        "redb.extractors.decompiler.apk.analyzer.JADXDecompiler"
    ), patch(
        "redb.extractors.decompiler.apk.analyzer.ApktoolDisassembler"
    ):
        from redb.extractors.decompiler.apk.analyzer import APKCodeAnalyzer
        analyzer = APKCodeAnalyzer("/fake/path.apk")
        analyzer.min_instructions = min_instructions
        return analyzer


def _make_mock_dex(strings):
    """Create a mock DEX object that returns the given strings."""
    dex = MagicMock()
    dex.get_strings.return_value = strings
    return dex


def _create_fake_apk_file():
    """Create a temporary file to act as a fake APK."""
    fd, path = tempfile.mkstemp(suffix=".apk")
    os.write(fd, b"PK\x03\x04fake apk content for hashing")
    os.close(fd)
    return path


# ---------------------------------------------------------------------------
# Tests: _string_entropy
# ---------------------------------------------------------------------------

class TestStringEntropy:
    """Tests for Shannon entropy computation."""

    def test_empty_string(self):
        analyzer = _make_analyzer()
        assert analyzer._string_entropy("") == 0.0

    def test_single_char(self):
        analyzer = _make_analyzer()
        assert analyzer._string_entropy("a") == 0.0

    def test_repeated_char(self):
        analyzer = _make_analyzer()
        assert analyzer._string_entropy("aaaa") == 0.0

    def test_two_equal_chars(self):
        analyzer = _make_analyzer()
        # "ab" -> entropy = 1.0
        assert abs(analyzer._string_entropy("ab") - 1.0) < 0.01

    def test_higher_entropy(self):
        analyzer = _make_analyzer()
        # More variety = higher entropy
        low = analyzer._string_entropy("aabb")
        high = analyzer._string_entropy("abcd")
        assert high > low

    def test_url_string_entropy(self):
        analyzer = _make_analyzer()
        entropy = analyzer._string_entropy("https://evil.com/payload")
        assert entropy > 2.0  # URLs have moderate entropy


# ---------------------------------------------------------------------------
# Tests: _extract_strings
# ---------------------------------------------------------------------------

class TestExtractStrings:
    """Tests for DEX string extraction."""

    def test_basic_extraction(self):
        analyzer = _make_analyzer()
        dex = _make_mock_dex(["hello", "world"])
        result = analyzer._extract_strings([dex])

        assert len(result) == 2
        assert result[0]["string"] == "hello"
        assert result[1]["string"] == "world"

    def test_string_fields_present(self):
        analyzer = _make_analyzer()
        dex = _make_mock_dex(["test_string"])
        result = analyzer._extract_strings([dex])

        entry = result[0]
        assert "string" in entry
        assert "string_encoding" in entry
        assert "string_offset" in entry
        assert "string_length" in entry
        assert "string_entropy" in entry
        assert entry["string_encoding"] == "UTF8"
        assert entry["string_length"] == len("test_string")

    def test_deduplication_across_dex(self):
        """Same string in multiple DEX files is only extracted once."""
        analyzer = _make_analyzer()
        dex1 = _make_mock_dex(["shared", "unique1"])
        dex2 = _make_mock_dex(["shared", "unique2"])
        result = analyzer._extract_strings([dex1, dex2])

        string_values = [s["string"] for s in result]
        assert string_values.count("shared") == 1
        assert "unique1" in string_values
        assert "unique2" in string_values
        assert len(result) == 3

    def test_empty_strings_filtered(self):
        analyzer = _make_analyzer()
        dex = _make_mock_dex(["", "valid", None, "also_valid"])
        result = analyzer._extract_strings([dex])

        string_values = [s["string"] for s in result]
        assert "" not in string_values
        assert None not in string_values
        assert "valid" in string_values
        assert "also_valid" in string_values

    def test_no_dex_files(self):
        analyzer = _make_analyzer()
        result = analyzer._extract_strings([])
        assert result == []

    def test_dex_get_strings_returns_none(self):
        analyzer = _make_analyzer()
        dex = MagicMock()
        dex.get_strings.return_value = None
        result = analyzer._extract_strings([dex])
        assert result == []

    def test_dex_get_strings_exception(self):
        analyzer = _make_analyzer()
        dex = MagicMock()
        dex.get_strings.side_effect = RuntimeError("corrupt DEX")
        result = analyzer._extract_strings([dex])
        assert result == []

    def test_entropy_computed(self):
        analyzer = _make_analyzer()
        dex = _make_mock_dex(["abcdefgh"])
        result = analyzer._extract_strings([dex])
        assert result[0]["string_entropy"] > 0.0

    def test_incremental_offsets(self):
        """String offsets are incremented sequentially."""
        analyzer = _make_analyzer()
        dex = _make_mock_dex(["a", "b", "c"])
        result = analyzer._extract_strings([dex])

        offsets = [s["string_offset"] for s in result]
        assert offsets == [0, 1, 2]

    def test_url_strings_preserved(self):
        """URL strings are extracted without modification."""
        analyzer = _make_analyzer()
        dex = _make_mock_dex([
            "https://evil.com/payload",
            "http://c2.malware.org/gate",
        ])
        result = analyzer._extract_strings([dex])
        strings = [s["string"] for s in result]
        assert "https://evil.com/payload" in strings
        assert "http://c2.malware.org/gate" in strings


# ---------------------------------------------------------------------------
# Tests: strings in extract() integration
# ---------------------------------------------------------------------------

class TestExtractStringsIntegration:
    """Tests that strings are included in extract() results."""

    @patch("redb.extractors.decompiler.apk.analyzer.APKCodeAnalyzer._run_androguard")
    @patch("redb.extractors.decompiler.apk.analyzer.ApktoolDisassembler")
    @patch("redb.extractors.decompiler.apk.analyzer.JADXDecompiler")
    def test_extract_includes_strings(self, mock_jadx, mock_apktool, mock_androguard):
        from redb.extractors.decompiler.apk.analyzer import APKCodeAnalyzer

        mock_dex = _make_mock_dex(["hello", "world"])
        mock_analysis = MagicMock()
        mock_analysis.get_methods.return_value = []
        mock_androguard.return_value = (MagicMock(), [mock_dex], mock_analysis)

        analyzer = APKCodeAnalyzer("/fake/path.apk", log=MagicMock())
        results = analyzer.extract()

        assert "strings" in results
        assert len(results["strings"]) == 2
        assert results["strings"][0]["string"] == "hello"
        analyzer.cleanup()

    @patch("redb.extractors.decompiler.apk.analyzer.APKCodeAnalyzer._run_androguard")
    @patch("redb.extractors.decompiler.apk.analyzer.ApktoolDisassembler")
    @patch("redb.extractors.decompiler.apk.analyzer.JADXDecompiler")
    def test_extract_strings_error_is_non_fatal(self, mock_jadx, mock_apktool, mock_androguard):
        from redb.extractors.decompiler.apk.analyzer import APKCodeAnalyzer

        # Return a bad dex list that will cause _extract_strings to fail
        mock_analysis = MagicMock()
        mock_analysis.get_methods.return_value = []
        # dexs is not iterable — triggers the except block
        mock_androguard.return_value = (MagicMock(), 42, mock_analysis)

        analyzer = APKCodeAnalyzer("/fake/path.apk", log=MagicMock())
        results = analyzer.extract()

        # Should not crash, strings should be empty, error recorded
        assert results["strings"] == []
        assert any(
            e.get("error_location") == "strings"
            for e in results["analysis_errors"]
        )
        analyzer.cleanup()


# ---------------------------------------------------------------------------
# Tests: DecompileAPK strings export table
# ---------------------------------------------------------------------------

class TestDecompileAPKStringsExport:
    """Tests for strings table in DecompileAPK.prepare_export_data()."""

    def _make_extractor_with_strings(self, strings_data):
        from redb.extractors.decompiler.DecompileAPK import DecompileAPK
        apk_path = _create_fake_apk_file()
        ext = DecompileAPK(apk_path, MagicMock())
        ext.analysis_results = {
            "sha256": "a" * 64,
            "sha1": "b" * 40,
            "md5": "c" * 32,
            "decompiled_content": [],
            "decompiled_refs": [],
            "smali_content": [],
            "smali_refs": [],
            "similarity_metrics": [],
            "strings": strings_data,
            "analysis_errors": [],
        }
        return ext, apk_path

    def test_strings_table_present(self):
        ext, path = self._make_extractor_with_strings([
            {"string": "hello", "string_encoding": "UTF8",
             "string_offset": 0, "string_length": 5, "string_entropy": 2.32}
        ])
        try:
            export = ext.prepare_export_data("ClickHouseExporter")
            assert "strings_raw" in export
        finally:
            os.unlink(path)

    def test_strings_table_schema(self):
        ext, path = self._make_extractor_with_strings([
            {"string": "hello", "string_encoding": "UTF8",
             "string_offset": 0, "string_length": 5, "string_entropy": 2.32}
        ])
        try:
            export = ext.prepare_export_data("ClickHouseExporter")
            sr = export["strings_raw"]
            assert sr["table"] == "code_binja_strings_raw"
            assert len(sr["column_names"]) == 8
            assert len(sr["column_type_names"]) == 8
            assert "sha256" in sr["column_names"]
            assert "string" in sr["column_names"]
            assert "string_raw" in sr["column_names"]
            assert "string_encoding" in sr["column_names"]
            assert "string_offset" in sr["column_names"]
            assert "string_length" in sr["column_names"]
            assert "string_raw_length" in sr["column_names"]
            assert "string_entropy" in sr["column_names"]
            assert len(sr["data"]) == 1
            assert len(sr["data"][0]) == 8
        finally:
            os.unlink(path)

    def test_strings_sha256_propagated(self):
        ext, path = self._make_extractor_with_strings([
            {"string": "test", "string_encoding": "UTF8",
             "string_offset": 0, "string_length": 4, "string_entropy": 2.0}
        ])
        try:
            export = ext.prepare_export_data("ClickHouseExporter")
            row = export["strings_raw"]["data"][0]
            assert row[0] == "a" * 64  # sha256 is first column
        finally:
            os.unlink(path)

    def test_empty_strings_not_exported(self):
        ext, path = self._make_extractor_with_strings([])
        try:
            export = ext.prepare_export_data("ClickHouseExporter")
            assert "strings_raw" not in export
        finally:
            os.unlink(path)


# ---------------------------------------------------------------------------
# Tests: IOCExtractorFromResults with APK format
# ---------------------------------------------------------------------------

class TestIOCExtractorAPKFormat:
    """Tests that IOCExtractorFromResults handles APK analysis_results."""

    def setup_method(self):
        self.log = MagicMock()

    def test_extract_iocs_from_apk_strings(self):
        """IOCs extracted from APK strings (same format as Binja)."""
        results = {
            "strings": [
                {"string": "https://evil.com/payload", "string_offset": 0},
                {"string": "Contact [email protected]", "string_offset": 1},
            ],
            "decompiled": [],
            "decompiled_content": [],
        }
        extractor = IOCExtractorFromResults(results, sha256="a" * 64, log=self.log)
        iocs = extractor.extract()

        url_iocs = [i for i in iocs if i.ioc_type == IOCType.URL]
        assert len(url_iocs) >= 1
        assert any("evil.com" in i.ioc_value for i in url_iocs)

    def test_extract_iocs_from_apk_decompiled_content(self):
        """IOCs extracted from APK's decompiled_content key."""
        results = {
            "strings": [],
            "decompiled": [],  # Binja format — empty
            "decompiled_content": [  # APK format
                {
                    "decompiled_method": "connect('https://c2.malware.org/gate');",
                    "decompiled_method_hash": "hash123",
                    "method_type": "USER",
                }
            ],
        }
        extractor = IOCExtractorFromResults(results, sha256="a" * 64, log=self.log)
        iocs = extractor.extract()

        url_iocs = [i for i in iocs if i.ioc_type == IOCType.URL]
        assert len(url_iocs) >= 1

    def test_apk_library_methods_skipped(self):
        """LIBRARY methods in decompiled_content are skipped."""
        results = {
            "strings": [],
            "decompiled": [],
            "decompiled_content": [
                {
                    "decompiled_method": "https://should-skip.com",
                    "decompiled_method_hash": "lib_hash",
                    "method_type": "LIBRARY",
                }
            ],
        }
        extractor = IOCExtractorFromResults(results, sha256="a" * 64, log=self.log)
        iocs = extractor.extract()
        assert len(iocs) == 0

    def test_combined_binja_and_apk_sources(self):
        """Both Binja and APK format decompiled entries are processed."""
        results = {
            "strings": [],
            "decompiled": [
                {
                    "decompiled_function": "call('https://binja.example.com');",
                    "decompiled_function_hash": "binja_hash",
                    "function_type": "USER",
                }
            ],
            "decompiled_content": [
                {
                    "decompiled_method": "send('https://apk.example.com');",
                    "decompiled_method_hash": "apk_hash",
                    "method_type": "USER",
                }
            ],
        }
        extractor = IOCExtractorFromResults(results, sha256="a" * 64, log=self.log)
        iocs = extractor.extract()

        url_iocs = [i for i in iocs if i.ioc_type == IOCType.URL]
        urls = [i.ioc_value for i in url_iocs]
        assert any("binja.example.com" in u for u in urls)
        assert any("apk.example.com" in u for u in urls)

    def test_apk_results_only(self):
        """Works when only APK keys are present (no 'decompiled' key)."""
        results = {
            "strings": [
                {"string": "https://evil.com", "string_offset": 0},
            ],
            "decompiled_content": [
                {
                    "decompiled_method": "x = 1",
                    "decompiled_method_hash": "h",
                    "method_type": "USER",
                }
            ],
        }
        extractor = IOCExtractorFromResults(results, sha256="a" * 64, log=self.log)
        iocs = extractor.extract()
        # Should not crash, and should find URL from strings
        url_iocs = [i for i in iocs if i.ioc_type == IOCType.URL]
        assert len(url_iocs) >= 1

    def test_source_type_is_string_for_strings(self):
        results = {
            "strings": [
                {"string": "https://evil.com/test", "string_offset": 42},
            ],
            "decompiled_content": [],
        }
        extractor = IOCExtractorFromResults(results, sha256="a" * 64, log=self.log)
        iocs = extractor.extract()
        url_iocs = [i for i in iocs if i.ioc_type == IOCType.URL]
        assert url_iocs[0].source_type == SourceType.STRING
        assert url_iocs[0].source_identifier == "42"

    def test_source_type_is_decompiled_for_methods(self):
        results = {
            "strings": [],
            "decompiled_content": [
                {
                    "decompiled_method": "https://evil.com/method",
                    "decompiled_method_hash": "myhash",
                    "method_type": "USER",
                }
            ],
        }
        extractor = IOCExtractorFromResults(results, sha256="a" * 64, log=self.log)
        iocs = extractor.extract()
        url_iocs = [i for i in iocs if i.ioc_type == IOCType.URL]
        assert url_iocs[0].source_type == SourceType.DECOMPILED_FUNCTION
        assert url_iocs[0].source_identifier == "myhash"


# ---------------------------------------------------------------------------
# Tests: Worker IOC wiring for APK
# ---------------------------------------------------------------------------

class TestWorkerAPKIOCWiring:
    """Tests that workers.py wires IOC extraction for APK."""

    def test_worker_source_contains_ioc_for_apk(self):
        """Verify workers.py source has IOC extraction in APK branch."""
        import inspect
        from redb import workers
        source = inspect.getsource(workers)
        # APK branch should import IOCExtractorFromResults
        assert "IOCExtractorFromResults" in source
        # Should be in the APK section (near DecompileAPK)
        apk_section = source[source.index("DecompileAPK("):]
        ioc_pos = apk_section.find("IOCExtractorFromResults")
        assert ioc_pos != -1, "IOCExtractorFromResults not found after DecompileAPK"