Catherine Faron-Zucker

118 papers A 1B 8C 5Misc 9Journal 23Unranked 66
YearRankTypeTitle / Venue / Authors
2020 Misc conf
SAC
Raphaël Gazzotti, Catherine Faron-Zucker, Fabien Gandon, Virginie Lacroix-Hugues, David Darmon
2019 Misc conf
SEMANTiCS
Molka Tounsi Dhouib, Catherine Faron-Zucker, Andrea G. B. Tettamanzi
2019 J jnl
Trans. Large Scale Data Knowl. Centered Syst.
Franck Michel, Catherine Faron-Zucker, Johan Montagnat
2019 conf
WWW (Companion Volume)
Franck Michel, Catherine Faron-Zucker, Olivier Corby, Fabien Gandon
2019 J jnl
CoRR
Fabien Gandon, Franck Michel, Olivier Corby, Michel Buffa, Andrea Tettamanzi, Catherine Faron-Zucker, Elena Cabrio, Serena Villata
2019 B conf
ESWC
Raphaël Gazzotti, Catherine Faron-Zucker, Fabien Gandon, Virginie Lacroix-Hugues, David Darmon
2019 conf
RJCIA
Raphaël Gazzotti, Elisa Noual, Catherine Faron-Zucker, Fabien Gandon, Alain Giboin, Virginie Lacroix-Hugues, David Darmon
2019 Misc conf
SEMANTiCS
Philippe A. Martin, Olivier Corby, Catherine Faron-Zucker
2019 J jnl
CoRR
Cédric Lopez, Molka Dhouib, Elena Cabrio, Catherine Faron-Zucker, Fabien Gandon, Frédérique Segond
2019 conf
EGC
Raphaël Gazzotti, Catherine Faron-Zucker, Fabien Gandon, Virginie Lacroix-Hugues, David Darmon
2018 conf
WWW (Companion Volume)
Oscar Rodríguez Rocha, Catherine Faron-Zucker
2018 conf
ESWC (Satellite Events)
Franck Michel, Catherine Faron-Zucker, Fabien Gandon
2018 conf
APIA
Molka Tounsi, Catherine Faron-Zucker, Andrea Tettamanzi
2018 conf
IC
Molka Tounsi, Catherine Faron-Zucker, Andrea Tettamanzi
2018 B conf
ITS
Oscar Rodríguez Rocha, Catherine Faron-Zucker, Alain Giboin
2018 J jnl
Inf.
Franck Michel, Catherine Faron-Zucker, Olivier Gargominy, Fabien Gandon
2018 B ed.
EKAW
Catherine Faron-Zucker, Chiara Ghidini, Amedeo Napoli, Yannick Toussaint
2018 J jnl
Rev. d'Intelligence Artif.
Cédric Lopez, Molka Tounsi Dhouib, Elena Cabrio, Catherine Faron-Zucker, Fabien Gandon, Frédéric Segond
2018 conf
LDOW@WWW
Franck Michel, Catherine Faron-Zucker, Fabien Gandon
2018 C conf
WEBIST
Géraud Fokou Pelap, Catherine Faron-Zucker, Fabien Gandon
2018 conf
WEBIST (Revised Selected Papers)
Géraud Fokou Pelap, Catherine Faron-Zucker, Fabien Gandon, Laurent Polese
2017 conf
S4BioDiv@ISWC
Franck Michel, Olivier Gargominy, Sandrine Tercerie, Catherine Faron-Zucker
2017 J jnl
Int. J. Semantic Web Inf. Syst.
Cristhian Figueroa, Iacopo Vagliano, Oscar Rodriguez Rocha, Marco Torchiano, Catherine Faron-Zucker, Juan Carlos Corrales, Maurizio Morisio
2017 book
Catherine Faron-Zucker
2017 conf
ISWC (1)
Olivier Corby, Catherine Faron-Zucker, Fabien Gandon
2017 J jnl
Web Intell.
Zide Meng, Fabien Gandon, Catherine Faron-Zucker
2017 J jnl
Comput. Informatics
Samia Beldjoudi, Hassina Seridi, Catherine Faron-Zucker
2017 conf
IC
Molka Tounsi, Cédric Lopez, Catherine Faron-Zucker, Elena Cabrio, Fabien Gandon, Frédérique Segond
2017 J jnl
Int. J. Approx. Reason.
Andrea G. B. Tettamanzi, Catherine Faron-Zucker, Fabien Gandon
2017 Misc ed.
SEMANTiCS
Rinke Hoekstra, Catherine Faron-Zucker, Tassilo Pellegrini, Victor de Boer
2016 conf
EKM@EKAW
Oscar Rodriguez Rocha, Catherine Faron-Zucker
2016 conf
EKAW (Satellite Events)
Oscar Rodriguez Rocha, Catherine Faron-Zucker, Géraud Fokou Pelap
2016 conf
WEBIST (2)
Franck Michel, Catherine Faron-Zucker, Johan Montagnat
2016 conf
DEXA (2)
Franck Michel, Catherine Faron-Zucker, Johan Montagnat
2016 Misc ed.
ICCS
Ollivier Haemmerlé, Gem Stapleton, Catherine Faron-Zucker
2016 J jnl
Rev. d'Intelligence Artif.
Marie-Hélène Abel, Catherine Faron-Zucker
2016 B conf
WI
Zide Meng, Fabien Gandon, Catherine Faron-Zucker
2016 conf
WWW (Companion Volume)
Jean-Michel Dalle, Catherine Faron-Zucker, Fabien Gandon, Mathieu Lacage, Zide Meng
2016 ed.
SWASH@ESWC
Isabelle Draelants, Catherine Faron-Zucker, Alexandre Monnin, Arnaud Zucker
2016 conf
CBRecSys@RecSys
Iacopo Vagliano, Cristhian Figueroa, Oscar Rodriguez Rocha, Marco Torchiano, Catherine Faron-Zucker, Maurizio Morisio
2016 conf
SWASH@ESWC
Catherine Faron-Zucker, Irene Pajón Leyra, Konstantina Poulida, Andrea G. B. Tettamanzi
2016 conf
ISWC (Posters & Demos)
Michel Buffa, Catherine Faron-Zucker, Thierry Bergeron, Hatim Aouzal
2016 J jnl
Rev. d'Intelligence Artif.
Olivier Corby, Catherine Faron-Zucker
2016 conf
RR
Olivier Corby, Catherine Faron-Zucker, Raphaël Gazzotti
2015 conf
ISWC (2)
Olivier Corby, Catherine Faron-Zucker, Fabien Gandon
2015 conf
WEBIST (Revised Selected Papers)
Olivier Corby, Catherine Faron-Zucker
2015 conf
WI-IAT (1)
Elena Cabrio, Catherine Faron-Zucker, Fabien Gandon, Amine Hallili, Andrea Tettamanzi
2015 J jnl
Soc. Netw. Anal. Min.
Zide Meng, Fabien Gandon, Catherine Faron-Zucker, Ge Song
2015 B conf
K-CAP
Andrea G. B. Tettamanzi, Catherine Faron-Zucker, Fabien Gandon
2015 ed.
SW4SH@ESWC
Arnaud Zucker, Isabelle Draelants, Catherine Faron-Zucker, Alexandre Monnin
2015 C conf
WEBIST
Olivier Corby, Catherine Faron-Zucker
2015 conf
WI-IAT (1)
Zide Meng, Fabien Gandon, Catherine Faron-Zucker
2015 conf
SW4SHD@ESWC
Molka Tounsi, Catherine Faron-Zucker, Arnaud Zucker, Serena Villata, Elena Cabrio
2015 conf
ESWC (Satellite Events)
Molka Tounsi, Catherine Faron-Zucker, Arnaud Zucker, Serena Villata, Elena Cabrio
2015 ed.
ESWC (Satellite Events)
Fabien Gandon, Christophe Guéret, Serena Villata, John G. Breslin, Catherine Faron-Zucker, Antoine Zimmermann
2015 conf
SW4SHD@ESWC
Cécile Callou, Franck Michel, Catherine Faron-Zucker, Chloé Martin, Johan Montagnat
2015 conf
WEBIST (Revised Selected Papers)
Franck Michel, Loïc Djimenou, Catherine Faron-Zucker, Johan Montagnat
2015 C conf
WEBIST
Franck Michel, Loïc Djimenou, Catherine Faron-Zucker, Johan Montagnat
2015 conf
IC
Olivier Corby, Catherine Faron-Zucker
2014 J jnl
Interact. Learn. Environ.
Khaled Halimi, Hassina Seridi-Bouchelaghem, Catherine Faron-Zucker
2014 J jnl
CoRR
Fabien Gandon, Michel Buffa, Elena Cabrio, Catherine Faron-Zucker, Alain Giboin, Nhan Le Thanh, Isabelle Mirbel, Peter Sander, Andrea G. B. Tettamanzi, Serena Villata
2014 B conf
ASONAM
Zide Meng, Fabien Gandon, Catherine Faron-Zucker, Ge Song
2014 ed.
IC
Catherine Faron-Zucker
2014 conf
IC
Oumy Seye, Catherine Faron-Zucker, Olivier Corby, Alban Gaignard
2014 conf
ISWC (Posters & Demos)
Amine Hallili, Elena Cabrio, Catherine Faron-Zucker
2014 conf
ISWC (Posters & Demos)
Zide Meng, Fabien Gandon, Catherine Faron-Zucker
2014 conf
IC
Olivier Corby, Catherine Faron-Zucker
2014 B conf
EKAW
Andrea G. B. Tettamanzi, Catherine Faron-Zucker, Fabien Gandon
2013 J jnl
CoRR
Khalil Riad Bouzidi, Bruno Fiés, Marc Bourdeau, Catherine Faron-Zucker, Nhan Le Thanh
2013 conf
ICEIS
Fabien Gandon, Michel Buffa, Elena Cabrio, Olivier Corby, Catherine Faron-Zucker, Alain Giboin, Nhan Le Thanh, Isabelle Mirbel, Peter Sander, Andrea Tettamanzi, Serena Villata
2013 J jnl
Ingénierie des Systèmes d Inf.
A. Authosserre-Cavarero, Frédéric Bertrand, Mireille Blay-Fornarino, Philippe Collet, Hubert Dubois, Stéphane Ducasse, Sophie Dupuy-Chessa, Catherine Faron-Zucker, Cyril Faucher, Jean-Yves Lafaye, Philippe Lahire, Olivier Le Goaër, Johan Montagnat, Anne-Marie Pinna-Dery
2013 ch.
Security and Privacy Preserving in Social Networks
Serena Villata, Luca Costabello, Fabien Gandon, Catherine Faron-Zucker, Michel Buffa
2013 J jnl
CoRR
Khalil Riad Bouzidi, Catherine Faron-Zucker, Bruno Fiés, Olivier Corby, Nhan Le Thanh
2012 conf
IC
Khalil Riad Bouzidi, Catherine Faron-Zucker, Bruno Fiés, Olivier Corby, Nhan Le Thanh
2012 conf
INFORSID
Annie Authossere, Frédéric Bertrand, Mireille Blay-Fornarino, Philippe Collet, Hubert Dubois, Stéphane Ducasse, Sophie Dupuy-Chessa, Catherine Faron-Zucker, Cyril Faucher, Jean-Yves Lafaye, Philippe Lahire, Olivier Le Goaër, Johan Montagnat, Anne-Marie Pinna-Dery
2012 conf
Web Intelligence
Olivier Corby, Alban Gaignard, Catherine Faron-Zucker, Johan Montagnat
2012 conf
EGC
Khalil Riad Bouzidi, Catherine Faron-Zucker, Bruno Fiés, Olivier Corby, Nhan Le Thanh
2012 Misc conf
AIMSA
Samia Beldjoudi, Hassina Seridi-Bouchelaghem, Catherine Faron-Zucker
2012 conf
EGC
Oumy Seye, Olivier Corby, Catherine Faron-Zucker
2012 J jnl
Future Internet
Khalil Riad Bouzidi, Bruno Fiés, Catherine Faron-Zucker, Alain Zarli, Nhan Le Thanh
2011 conf
RR
Khalil Riad Bouzidi, Catherine Faron-Zucker, Bruno Fiés, Nhan Le Thanh
2011 conf
EGC
Pavel Arapov, Sébastien Comos, Olivier Corby, Nicolas Delaforge, Guillaume Erétéo, Catherine Faron-Zucker, Michel Buffa, Fabien Gandon, Guillaume Husson, Freddy Limpens
2011 conf
SPIM
Samia Beldjoudi, Hassina Seridi, Catherine Faron-Zucker
2011 C conf
WEBIST
Christian Brel, Anne-Marie Pinna-Dery, Catherine Faron-Zucker, Philippe Renevier-Gonin, Michel Riveill
2011 J jnl
Interact. Learn. Environ.
Amel Yessad, Catherine Faron-Zucker, Rose Dieng-Kuntz, Med Tayeb Laskri
2011 conf
CASoN
Khaled Halimi, Hassina Seridi, Catherine Faron-Zucker
2010 conf
HCSE
Christian Brel, Philippe Renevier-Gonin, Audrey Occello, Anne-Marie Pinna-Dery, Catherine Faron-Zucker, Michel Riveill
2010 conf
EGC
Michel Buffa, Catherine Faron-Zucker, Anna Kolomoyskaya
2010 conf
EGC
Olivier Corby, Catherine Faron-Zucker
2010 J jnl
Int. J. Knowl. Eng. Soft Data Paradigms
Anastasiya Yurchyshyna, Catherine Faron-Zucker, Nhan Le Thanh, Alain Zarli
2010 conf
EGC (best of volume)
Michel Buffa, Catherine Faron-Zucker
2010 conf
Web Intelligence
Olivier Corby, Catherine Faron-Zucker
2010 conf
RED
Pascal Neveu, Caroline Domerg, Juliette Fabre, Vincent Nègre, Emilie Gennari, Anne Tireau, Olivier Corby, Catherine Faron-Zucker, Isabelle Mirbel
2009 conf
WEBIST (Selected Papers)
Anastasiya Yurchyshyna, Catherine Faron-Zucker, Nhan Le Thanh, Alain Zarli
2009 conf
Actes d'IC
Olivier Corby, Catherine Faron-Zucker, Isabelle Mirbel
2009 conf
ICEIS (2)
Olivier Corby, Catherine Faron-Zucker, Isabelle Mirbel
2009 C conf
WEBIST
Anastasiya Yurchyshyna, Alain Zarli, Catherine Faron-Zucker, Nhan Le Thanh
2008 J jnl
Int. J. Web Based Learn. Teach. Technol.
Amel Yessad, Catherine Faron-Zucker, Rose Dieng-Kuntz, Med Tayeb Laskri
2008 Misc conf
ICCS
Jean-François Baget, Olivier Corby, Rose Dieng-Kuntz, Catherine Faron-Zucker, Fabien Gandon, Alain Giboin, Alain Gutierrez, Michel Leclère, Marie-Laure Mugnier, Rallou Thomopoulos
2008 conf
ICEIS (2)
Catherine Faron-Zucker, Nhan Le Thanh, Anastasiya Yurchyshyna, Alain Zarli
2008 conf
ICCS Supplement
Hacène Cherfi, Olivier Corby, Catherine Faron-Zucker, Khaled Khelif, M. T. Nguyen
2008 conf
CAiSE Forum
Anastasiya Yurchyshyna, Catherine Faron-Zucker, Nhan Le Thanh, Alain Zarli
2008 conf
KES (1)
Anastasiya Yurchyshyna, Catherine Faron-Zucker, Nhan Le Thanh, Alain Zarli
2008 conf
EGC
Catherine Faron-Zucker, Anastasiya Yurchyshyna, Nhan Le Thanh, Celson Lima
2008 conf
Actes d'IC
Anastasiya Yurchyshyna, Catherine Faron-Zucker, Isabelle Mirbel, Birahim Sall, Nhan Le Thanh, Alain Zarli
2007 Misc conf
ICCS
Olivier Corby, Catherine Faron-Zucker
2007 conf
Web Intelligence
Olivier Corby, Catherine Faron-Zucker
2006 B conf
ICALT
Sylvain Dehors, Catherine Faron-Zucker
2006 J jnl
IEEE Intell. Syst.
Olivier Corby, Rose Dieng-Kuntz, Catherine Faron-Zucker, Fabien Gandon
2005 conf
EGC
Laurent Alamarguy, Rose Dieng-Kuntz, Catherine Faron-Zucker
2005 conf
KES (3)
Laurent Alamarguy, Rose Dieng-Kuntz, Catherine Faron-Zucker
2005 conf
EGC (Ateliers)
Sylvain Dehors, Catherine Faron-Zucker, Alain Giboin, Jean Paul Stromboni
2004 A conf
ECAI
Olivier Corby, Rose Dieng-Kuntz, Catherine Faron-Zucker
2003 J jnl
Obj. Logiciel Base données Réseaux
Alexandre Delteil, Catherine Faron-Zucker, Rose Dieng
2001 conf
OIS@IJCAI
Alexandre Delteil, Catherine Faron-Zucker
2001 conf
WebNet
Alexandre Delteil, Catherine Faron-Zucker, Rose Dieng
2001 Misc conf
ICCS
Alexandre Delteil, Catherine Faron-Zucker, Rose Dieng
2001 conf
Workshop on Ontology Learning
Alexandre Delteil, Catherine Faron-Zucker, Rose Dieng
tests/unit/test_apk_decompile_extractor.py
← Index tests/unit/test_apk_decompile_extractor.py python
"""
Unit tests for DecompileAPK extractor, export data, and ClickHouse schemas.

Tests the main DecompileAPK extractor class following the DecompileBinja test pattern.
All analysis is mocked — no JADX/apktool/Java installation required.
"""
import hashlib
import os
import tempfile
import zipfile

import pytest
from unittest.mock import MagicMock, patch, PropertyMock
from dataclasses import asdict

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


def _create_fake_apk_file():
    """Create a minimal APK file for testing and return its path."""
    tmpdir = tempfile.mkdtemp()
    apk_path = os.path.join(tmpdir, "test.apk")
    with zipfile.ZipFile(apk_path, "w") as zf:
        zf.writestr("AndroidManifest.xml", b"<manifest/>")
        zf.writestr("classes.dex", b"dex\n035\x00" + b"\x00" * 100)
    return apk_path


# ============================================================================
# DecompileAPK Extractor Tests
# ============================================================================

class TestDecompileAPK:
    """Tests for the DecompileAPK extractor class."""

    def test_tag(self):
        from redb.extractors.decompiler.DecompileAPK import DecompileAPK
        apk_path = _create_fake_apk_file()
        try:
            ext = DecompileAPK(apk_path, MagicMock())
            assert ext.tag() == "apk_decompiled"
        finally:
            os.unlink(apk_path)

    def test_get_clickhouse_table(self):
        from redb.extractors.decompiler.DecompileAPK import DecompileAPK
        apk_path = _create_fake_apk_file()
        try:
            ext = DecompileAPK(apk_path, MagicMock())
            assert ext.get_clickhouse_table() is None
        finally:
            os.unlink(apk_path)

    def test_timeout_default(self):
        from redb.extractors.decompiler.DecompileAPK import DecompileAPK
        apk_path = _create_fake_apk_file()
        try:
            ext = DecompileAPK(apk_path, MagicMock())
            assert ext.APK_DECOMPILE_TIMEOUT == 600
        finally:
            os.unlink(apk_path)

    def test_timeout_from_env(self):
        from redb.extractors.decompiler.DecompileAPK import DecompileAPK
        apk_path = _create_fake_apk_file()
        try:
            with patch.dict(os.environ, {"APK_DECOMPILE_TIMEOUT": "900"}):
                ext = DecompileAPK(apk_path, MagicMock())
                assert ext.APK_DECOMPILE_TIMEOUT == 900
        finally:
            os.unlink(apk_path)

    def test_timeout_invalid_env(self):
        from redb.extractors.decompiler.DecompileAPK import DecompileAPK
        apk_path = _create_fake_apk_file()
        try:
            with patch.dict(os.environ, {"APK_DECOMPILE_TIMEOUT": "not_a_number"}):
                ext = DecompileAPK(apk_path, MagicMock())
                assert ext.APK_DECOMPILE_TIMEOUT == 600
        finally:
            os.unlink(apk_path)

    def test_calculate_md5(self):
        from redb.extractors.decompiler.DecompileAPK import DecompileAPK
        apk_path = _create_fake_apk_file()
        try:
            ext = DecompileAPK(apk_path, MagicMock())
            result = ext.calculate_md5("test_string")
            assert len(result) == 32
            assert result == hashlib.md5(b"test_string").hexdigest()
        finally:
            os.unlink(apk_path)

    def test_context_manager(self):
        from redb.extractors.decompiler.DecompileAPK import DecompileAPK
        apk_path = _create_fake_apk_file()
        try:
            with DecompileAPK(apk_path, MagicMock()) as ext:
                assert ext is not None
        finally:
            os.unlink(apk_path)

    def test_cleanup_run(self):
        from redb.extractors.decompiler.DecompileAPK import DecompileAPK
        apk_path = _create_fake_apk_file()
        try:
            ext = DecompileAPK(apk_path, MagicMock())
            ext.analyzer = MagicMock()
            ext.cleanup_run()
            assert ext.analyzer is None
        finally:
            os.unlink(apk_path)


# ============================================================================
# Export Data Tests
# ============================================================================

class TestPrepareExportData:
    """Tests for DecompileAPK.prepare_export_data() ClickHouse schemas."""

    def _make_extractor_with_results(self):
        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_method_hash": "d" * 64,
                    "decompiled_method": "public void foo() {}",
                    "decompiled_method_type": "USER",
                    "decompiled_has_string_encryption": False,
                    "decompiled_has_reflection_calls": False,
                    "decompiled_excessive_goto_count": False,
                }
            ],
            "decompiled_refs": [
                {
                    "decompiled_method_hash": "d" * 64,
                    "smali_method_hash": "e" * 64,
                    "decompiled_class_name": "com.example.Foo",
                    "decompiled_method_name": "foo",
                    "decompiled_method_signature": "()V",
                    "decompiled_method_prototype": "void foo()",
                    "functions_caller": ["bar"],
                    "functions_call": ["baz"],
                }
            ],
            "smali_content": [
                {
                    "smali_method_hash": "e" * 64,
                    "smali_method": "invoke-virtual {p0}, Lfoo;->bar()V\nreturn-void",
                    "smali_method_type": "USER",
                    "smali_instructions_count": 10,
                    "smali_register_count": 3,
                    "smali_has_string_encryption": False,
                    "smali_has_reflection_calls": False,
                    "smali_excessive_goto_count": False,
                    "smali_flattened_score": 0.0,
                    "smali_mba_score": 0.0,
                }
            ],
            "smali_refs": [
                {
                    "smali_method_hash": "e" * 64,
                    "decompiled_method_hash": "d" * 64,
                    "smali_class_name": "com.example.Foo",
                    "smali_method_name": "foo",
                    "smali_method_signature": "()V",
                    "ssdeep_smali": None,
                    "tlsh_smali": None,
                }
            ],
            "similarity_metrics": [
                {
                    "smali_method_hash": "e" * 64,
                    "cyclomatic_complexity": 2,
                    "ssdeep_smali": None,
                    "tlsh_smali": None,
                    "ssdeep_smali_normalized": None,
                    "tlsh_smali_normalized": None,
                    "minhash": [],
                }
            ],
            "cfg": [
                {
                    "smali_method_hash": "e" * 64,
                    "cfg_topology_hash": b'\x01' * 16,
                    "block_count": 3,
                    "edge_count": 3,
                    "cfg_instructions_count": 10,
                    "call_count": 1,
                    "cyclomatic_complexity": 2,
                    "loop_count": 0,
                    "max_depth": 2,
                    "max_fan_out": 2,
                    "md_index_topdown": 12345,
                    "md_index_bottomup": 67890,
                    "prime_product_smali": 999,
                    "cfg_feature_tlsh": None,
                    "wl_minhash": [0] * 128,
                    "bb_features": [[5, 1, 0, 1, 1, 0, 0, 2]] * 3,
                    "cfg_adjacency": [0x00000001, 0x00000002],
                }
            ],
            "strings": [
                {
                    "string": "https://evil.example.com/payload",
                    "string_encoding": "UTF8",
                    "string_offset": 0,
                    "string_length": 31,
                    "string_entropy": 3.95,
                }
            ],
            "analysis_errors": [
                {
                    "class_name": "com.example.Foo",
                    "method_name": "bar",
                    "error_location": "jadx",
                    "error_message": "timeout",
                    "error_type": "TimeoutError",
                }
            ],
        }
        return ext, apk_path

    def test_returns_none_when_no_results(self):
        from redb.extractors.decompiler.DecompileAPK import DecompileAPK
        apk_path = _create_fake_apk_file()
        try:
            ext = DecompileAPK(apk_path, MagicMock())
            assert ext.prepare_export_data("ClickHouseExporter") is None
        finally:
            os.unlink(apk_path)

    def test_multi_table_flag(self):
        ext, apk_path = self._make_extractor_with_results()
        try:
            export = ext.prepare_export_data("ClickHouseExporter")
            assert export["multi_table"] is True
        finally:
            os.unlink(apk_path)

    def test_all_table_keys_present(self):
        ext, apk_path = self._make_extractor_with_results()
        try:
            export = ext.prepare_export_data("ClickHouseExporter")
            assert "decompiled_content" in export
            assert "decompiled_refs" in export
            assert "smali_content" in export
            assert "smali_refs" in export
            assert "method_similarity_metrics" in export
            assert "strings_raw" in export
            assert "analysis_errors" in export
        finally:
            os.unlink(apk_path)

    def test_decompiled_content_schema(self):
        ext, apk_path = self._make_extractor_with_results()
        try:
            export = ext.prepare_export_data("ClickHouseExporter")
            dc = export["decompiled_content"]
            assert dc["table"] == "code_apk_decompiled_methods_content"
            assert len(dc["column_names"]) == 7
            assert len(dc["column_type_names"]) == 7
            assert "decompiled_method_hash" in dc["column_names"]
            assert "decompiled_method" in dc["column_names"]
            assert "analysis_date" in dc["column_names"]
            assert len(dc["data"]) == 1
            assert len(dc["data"][0]) == 7
        finally:
            os.unlink(apk_path)

    def test_decompiled_refs_schema(self):
        ext, apk_path = self._make_extractor_with_results()
        try:
            export = ext.prepare_export_data("ClickHouseExporter")
            dr = export["decompiled_refs"]
            assert dr["table"] == "code_apk_decompiled_methods_references"
            assert len(dr["column_names"]) == 10
            assert len(dr["column_type_names"]) == 10
            assert "sha256" in dr["column_names"]
            assert "sha1" not in dr["column_names"]
            assert "md5" not in dr["column_names"]
            assert "functions_caller" in dr["column_names"]
            assert "functions_call" in dr["column_names"]
        finally:
            os.unlink(apk_path)

    def test_smali_content_schema(self):
        ext, apk_path = self._make_extractor_with_results()
        try:
            export = ext.prepare_export_data("ClickHouseExporter")
            sc = export["smali_content"]
            assert sc["table"] == "code_apk_smali_methods_content"
            assert len(sc["column_names"]) == 11
            assert len(sc["column_type_names"]) == 11
            assert "smali_instructions_count" in sc["column_names"]
            assert "smali_register_count" in sc["column_names"]
            assert "smali_flattened_score" in sc["column_names"]
            assert "smali_mba_score" in sc["column_names"]
        finally:
            os.unlink(apk_path)

    def test_smali_refs_schema(self):
        ext, apk_path = self._make_extractor_with_results()
        try:
            export = ext.prepare_export_data("ClickHouseExporter")
            sr = export["smali_refs"]
            assert sr["table"] == "code_apk_smali_methods_references"
            assert len(sr["column_names"]) == 7
            assert len(sr["column_type_names"]) == 7
            assert "sha1" not in sr["column_names"]
            assert "md5" not in sr["column_names"]
            assert "ssdeep_smali" not in sr["column_names"]
            assert "tlsh_smali" not in sr["column_names"]
        finally:
            os.unlink(apk_path)

    def test_similarity_metrics_schema(self):
        ext, apk_path = self._make_extractor_with_results()
        try:
            export = ext.prepare_export_data("ClickHouseExporter")
            sm = export["method_similarity_metrics"]
            assert sm["table"] == "code_apk_method_similarity_metrics"
            assert len(sm["column_names"]) == 7
            assert "minhash" in sm["column_names"]
            assert "ssdeep_smali_normalized" in sm["column_names"]
            assert "tlsh_smali_normalized" in sm["column_names"]
            # cyclomatic_complexity moved to cfg table
            assert "cyclomatic_complexity" not in sm["column_names"]
            assert "block_count" not in sm["column_names"]
            assert "loop_count" not in sm["column_names"]
        finally:
            os.unlink(apk_path)

    def test_analysis_errors_schema(self):
        ext, apk_path = self._make_extractor_with_results()
        try:
            export = ext.prepare_export_data("ClickHouseExporter")
            ae = export["analysis_errors"]
            assert ae["table"] == "code_apk_analysis_errors"
            assert len(ae["column_names"]) == 9
            assert "error_hash" in ae["column_names"]
            assert "status" in ae["column_names"]
        finally:
            os.unlink(apk_path)

    def test_sha256_propagated_to_refs(self):
        ext, apk_path = self._make_extractor_with_results()
        try:
            export = ext.prepare_export_data("ClickHouseExporter")
            # Check that sha256 from analysis_results appears in ref rows
            dr_row = export["decompiled_refs"]["data"][0]
            assert dr_row[0] == "a" * 64  # sha256
            # sha1 and md5 no longer included in refs
            assert "sha1" not in export["decompiled_refs"]["column_names"]
            assert "md5" not in export["decompiled_refs"]["column_names"]
        finally:
            os.unlink(apk_path)

    def test_empty_tables_not_included(self):
        from redb.extractors.decompiler.DecompileAPK import DecompileAPK
        apk_path = _create_fake_apk_file()
        try:
            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": [],
                "cfg": [],
                "strings": [],
                "analysis_errors": [],
            }
            export = ext.prepare_export_data("ClickHouseExporter")
            assert export == {"multi_table": True}
        finally:
            os.unlink(apk_path)

    def test_non_clickhouse_exporter_returns_none(self):
        ext, apk_path = self._make_extractor_with_results()
        try:
            result = ext.prepare_export_data("ElasticsearchExporter")
            assert result is None
        finally:
            os.unlink(apk_path)


# ============================================================================
# Dataclass Tests
# ============================================================================

class TestAPKCodeAnalysisDataclasses:
    """Tests for the new APK code analysis dataclasses."""

    def test_decompiled_method_content(self):
        from redb.models.dataclasses import APKDecompiledMethodContent
        dc = APKDecompiledMethodContent(
            decompiled_method_hash="a" * 64,
            decompiled_method="public void foo() {}",
            decompiled_method_type="USER",
        )
        assert dc.decompiled_has_string_encryption is False
        assert dc.decompiled_has_reflection_calls is False
        assert dc.decompiled_excessive_goto_count is False

    def test_decompiled_method_content_populated(self):
        from redb.models.dataclasses import APKDecompiledMethodContent
        dc = APKDecompiledMethodContent(
            decompiled_method_hash="a" * 64,
            decompiled_method="public void foo() {}",
            decompiled_method_type="USER",
            decompiled_has_string_encryption=True,
            decompiled_has_reflection_calls=True,
            decompiled_excessive_goto_count=True,
        )
        assert dc.decompiled_has_string_encryption is True
        d = asdict(dc)
        assert d["decompiled_method_hash"] == "a" * 64

    def test_decompiled_method_reference(self):
        from redb.models.dataclasses import APKDecompiledMethodReference
        dr = APKDecompiledMethodReference(
            sha256="a" * 64,
            sha1="b" * 40,
            md5="c" * 32,
            decompiled_method_hash="d" * 64,
        )
        assert dr.functions_caller == []
        assert dr.functions_call == []
        assert dr.smali_method_hash is None

    def test_smali_method_content(self):
        from redb.models.dataclasses import APKSmaliMethodContent
        sc = APKSmaliMethodContent(
            smali_method_hash="e" * 64,
            smali_method="invoke-virtual {p0}, Lfoo;->bar()V",
            smali_method_type="USER",
            smali_instructions_count=5,
            smali_register_count=3,
        )
        assert sc.smali_instructions_count == 5
        assert sc.smali_register_count == 3

    def test_smali_method_reference(self):
        from redb.models.dataclasses import APKSmaliMethodReference
        sr = APKSmaliMethodReference(
            sha256="a" * 64,
            sha1="b" * 40,
            md5="c" * 32,
            smali_method_hash="e" * 64,
        )
        assert sr.decompiled_method_hash is None
        assert sr.ssdeep_smali is None
        assert sr.tlsh_smali is None

    def test_method_similarity_metrics(self):
        from redb.models.dataclasses import APKMethodSimilarityMetrics
        ms = APKMethodSimilarityMetrics(smali_method_hash="e" * 64)
        assert ms.cyclomatic_complexity is None
        assert ms.ssdeep_smali_normalized is None
        assert ms.tlsh_smali_normalized is None
        assert ms.minhash == []

    def test_code_analysis_error(self):
        from redb.models.dataclasses import APKCodeAnalysisError
        err = APKCodeAnalysisError(
            sha256="a" * 64,
            error_location="jadx",
            error_message="timeout",
        )
        assert err.class_name is None
        assert err.method_name is None
        assert err.error_type is None


# ============================================================================
# Tag Enum Tests
# ============================================================================

class TestAPKDecompiledTag:
    """Tests for the APK_DECOMPILED tag enum."""

    def test_tag_exists(self):
        from redb.extractors.enum import Tag
        assert hasattr(Tag, "APK_DECOMPILED")

    def test_tag_value(self):
        from redb.extractors.enum import Tag
        assert Tag.APK_DECOMPILED.value == "apk_decompiled"


# ============================================================================
# Worker Integration Tests
# ============================================================================

class TestAPKWorkerDecompileIntegration:
    """Tests that DecompileAPK is registered in workers.py."""

    def test_module_registered(self):
        """Verify DecompileAPK is discoverable via the extractor registry."""
        from redb.extractor_registry import get_extractor_class
        cls = get_extractor_class("DecompileAPK")
        assert cls is not None, "DecompileAPK not found in extractor registry"
        assert cls.__name__ == "DecompileAPK"

    def test_worker_apk_dispatch_includes_decompile(self):
        import inspect
        import redb.workers as workers
        source = inspect.getsource(workers.process_binary_file)
        assert "DecompileAPK" in source