Hans Akkermans

88 papers A* 1A 9B 12C 4Misc 2Journal 19Unranked 37
YearRankTypeTitle / Venue / Authors
2022 conf
WebSci
George Vlad Stan, André Baart, Francis Dittoh, Hans Akkermans, Anna Bon
2022 ch.
Perspectives on Digital Humanism
Hans Akkermans, Jaap Gordijn, Anna Bon
2021 conf
ICICT (1)
Francis Dittoh, Hans Akkermans, Victor de Boer, Anna Bon, Wendelien Tuyp, André Baart
2020 conf
WebSci (Companion)
Anna Bon, Hans Akkermans
2020 C conf
COMPASS
Francis Dittoh, Hans Akkermans, Victor de Boer, Anna Bon, Wendelien Tuyp, André Baart
2019 conf
ICT4D (2)
Anna Bon, Jaap Gordijn, Hans Akkermans, Victor de Boer, André Baart, Cheah Wai Shiang, San-Nah Sze
2019 conf
ICT4D (2)
Anna Bon, Hans Akkermans
2018 conf
WEBIST (Revised Selected Papers)
André Baart, Anna Bon, Victor de Boer, Francis Dittoh, Wendelien Tuijp, Hans Akkermans
2018 conf
P-ICT4D@WebSci
André Baart, Anna Bon, Victor de Boer, Wendelien Tuyp, Hans Akkermans
2018 C conf
WEBIST
André Baart, Anna Bon, Victor de Boer, Wendelien Tuijp, Hans Akkermans
2018 ed.
WebSci
Hans Akkermans, Kathy Fontaine, Ivar E. Vermeulen, Geert-Jan Houben, Matthew S. Weber
2016 conf
CAiSE Forum
Anna Bon, Jaap Gordijn, Hans Akkermans
2016 J jnl
Complex Syst. Informatics Model. Q.
Anna Bon, Hans Akkermans, Jaap Gordijn
2015 J jnl
Semantic Web
Victor de Boer, Nana Baah Gyan, Anna Bon, Wendelien Tuyp, Chris J. van Aart, Hans Akkermans
2013 conf
WebSci
Hans Akkermans, Rena Bakhshi
2013 B conf
REFSQ
Anna Bon, Victor de Boer, Nana Baah Gyan, Chris J. van Aart, Pieter De Leenheer, Wendelien Tuyp, Stéphane Boyera, Max Froumentin, Aman Grewal, Mary Allen, Amadou Tangara, Hans Akkermans
2013 conf
WebSci
Nana Baah Gyan, Victor de Boer, Anna Bon, Chris J. van Aart, Hans Akkermans, Stéphane Boyera, Max Froumentin, Aman Grewal, Mary Allen
2013 J jnl
Appl. Ontology
Sybren de Kinderen, Pieter De Leenheer, Jaap Gordijn, Hans Akkermans, Rose-Marie Dröes, Franka Meiland
2012 A conf
CAiSE
Iván S. Razo-Zapata, Pieter De Leenheer, Jaap Gordijn, Hans Akkermans
2012 A conf
CAiSE
Victor de Boer, Pieter De Leenheer, Anna Bon, Nana Baah Gyan, Chris J. van Aart, Christophe Guéret, Wendelien Tuyp, Stéphane Boyera, Mary Allen, Hans Akkermans
2012 ch.
Handbook of Service Description
Iván S. Razo-Zapata, Pieter De Leenheer, Jaap Gordijn, Hans Akkermans
2012 conf
ESWC (Satellite Events)
Anna Bon, Victor de Boer, Pieter De Leenheer, Chris J. van Aart, Nana Baah Gyan, Max Froumentin, Stéphane Boyera, Mary Allen, Hans Akkermans
2012 J jnl
Requir. Eng.
Vincent Pijpers, Pieter De Leenheer, Jaap Gordijn, Hans Akkermans
2012 conf
WebSci
Hans Akkermans
2011 J jnl
CoRR
Hans Akkermans, Fredrik Ygge
2011 B conf
CEC
Iván S. Razo-Zapata, Jaap Gordijn, Pieter De Leenheer, Hans Akkermans
2011 conf
WebSci
Hans Akkermans, Nana Baah Gyan, Anna Bon, Wendelien Tuyp, Aman Grewal, Stéphane Boyera, Mary Allen
2011 conf
CAiSE Workshops
Iván S. Razo-Zapata, Pieter De Leenheer, Jaap Gordijn, Hans Akkermans
2010 conf
ICST E-Energy
Gerard Peppink, René Kamphuis, Koen Kok, Aris L. Dimeas, Evangelos Karfopoulos, Nikos D. Hatziargyriou, Nouredine Hadjsaid, Raphaël Caire, Rune Gustavsson, Josep M. Salas, Hugo Niesing, Jörgen van der Velde, Llani Tena, Frits Bliek, Marcel Eijgelaar, Luc Hamilton, Hans Akkermans
2010 Misc conf
PoEM
Jaap Gordijn, Floris de Haan, Sybren de Kinderen, Hans Akkermans
2009 conf
Bled eConference
Vincent Pijpers, Jaap Gordijn, Hans Akkermans
2009 conf
ICEIS (3)
Sybren de Kinderen, Jaap Gordijn, Hans Akkermans
2009 B conf
RCIS
Jaap Gordijn, Vincent Pijpers, Hans Akkermans
2009 J jnl
Int. J. Comput. Sci. Appl.
Vincent Pijpers, Jaap Gordijn, Hans Akkermans
2008 conf
HICSS
Koen Kok, Zsófia Derzsi, Jaap Gordijn, Maarten Hommelberg, Cor Warmer, René Kamphuis, Hans Akkermans
2008 Misc conf
PoEM
Vincent Pijpers, Jaap Gordijn, Hans Akkermans
2008 C conf
ICEC
Vincent Pijpers, Jaap Gordijn, Hans Akkermans
2008 conf
FOMI
Hans Akkermans
2008 conf
FIS
Jaap Gordijn, Sybren de Kinderen, Vincent Pijpers, Hans Akkermans
2007 A conf
CAiSE
Zsófia Derzsi, Jaap Gordijn, Koen Kok, Hans Akkermans, Yao-Hua Tan
2006 conf
Bled eConference
Sybren de Kinderen, Jaap Gordijn, Hans Akkermans
2006 B conf
EKAW
Hans Akkermans, Jaap Gordijn
2006 A conf
RE
Hans Akkermans, Jaap Gordijn
2005 A conf
CAiSE
Ziv Baida, Jaap Gordijn, Hanne Sæle, Hans Akkermans, Andrei Z. Morch
2005 J jnl
Int. J. E Bus. Res.
Ziv Baida, Jaap Gordijn, Hans Akkermans, Hanne Sæle, Andrei Z. Morch
2005 conf
EC-Web
Carlos Pedrinaci, Ziv Baida, Hans Akkermans, Amaia Bernaras, Jaap Gordijn, Tim Smithers
2005 conf
Integrating Ontologies
Hans Akkermans
2005 conf
GI Jahrestagung (2)
Ziv Baida, Hans Akkermans, Jaap Gordijn
2004 A conf
RE
Jaap Gordijn, Vera Kartseva, Joost Schildwacht, Roel J. Wieringa, Hans Akkermans
2004 A conf
AAMAS
Hans Akkermans, Jos Schreinemakers, Koen Kok
2004 A conf
CAiSE
Ziv Baida, Jaap Gordijn, Hanne Sæle, Andrei Z. Morch, Hans Akkermans
2004 conf
Bled eConference
Hans Akkermans, Ziv Baida, Jaap Gordijn, Andrei Z. Morch, Hanne Sæle
2004 ch.
Handbook on Ontologies
Peter Mika, Victor Iosif, York Sure, Hans Akkermans
2004 J jnl
Knowl. Eng. Rev.
Peter Mika, Hans Akkermans
2004 J jnl
IEEE Intell. Syst.
Hans Akkermans, Ziv Baida, Jaap Gordijn, Nieves Peña, Ander Altuna, Iñaki Laresgoiti
2003 conf
Bled eConference
Hans Akkermans, Jaap Gordijn, Vera Kartseva
2003 conf
HICSS
Jaap Gordijn, Hans Akkermans
2003 C conf
ICEC
Ziv Baida, Hans Akkermans, Jaap Gordijn
2002 conf
IIP
Hans Akkermans
2002 conf
ICEIMT
Hans Akkermans
2001 J jnl
IEEE Intell. Syst.
Hans Akkermans
2001 B conf
K-CAP
Jaap Gordijn, Hans Akkermans
2001 conf
HICSS
Jaap Gordijn, Hans de Bruin, Hans Akkermans
2000 conf
ER (Workshops)
Jaap Gordijn, Hans Akkermans, Hans van Vliet
2000 J jnl
Auton. Agents Multi Agent Syst.
Fredrik Ygge, Hans Akkermans
2000 conf
EC-Web
Jaap Gordijn, Hans Akkermans, Hans van Vliet, Edwin Paalvast
2000 conf
HICSS
Jaap Gordijn, Hans Akkermans, Hans van Vliet
2000 B conf
EKAW
Jaap Gordijn, Hans Akkermans, Hans van Vliet
1999 J jnl
J. Artif. Intell. Res.
Fredrik Ygge, Hans Akkermans
1998 J jnl
IEEE Intell. Syst.
Anita Pos, Hans Akkermans, Jan L. Top
1998 conf
ICMAS
Fredrik Ygge, Hans Akkermans
1997 B conf
EKAW
Pim Borst, Hans Akkermans
1997 J jnl
Int. J. Hum. Comput. Stud.
Pim Borst, Hans Akkermans, Jan L. Top
1997 conf
MAAMAW
Fredrik Ygge, Hans Akkermans
1997 B conf
EKAW
Anita Pos, Hans Akkermans, Remco Straatman
1996 conf
ESM
Anita Pos, Hans Akkermans
1996 B conf
EKAW
Jan Benjamin, Pim Borst, Hans Akkermans, Bob J. Wielinga
1996 J jnl
Knowl. Based Syst.
Anita Pos, Pim Borst, Jan L. Top, Hans Akkermans
1994 B conf
EKAW
Guus Schreiber, Bob J. Wielinga, Hans Akkermans, Walter Van de Velde, Anjo Anjewierden
1994 J jnl
IEEE Expert
Guus Schreiber, Bob J. Wielinga, Robert de Hoog, Hans Akkermans, Walter Van de Velde
1994 J jnl
Int. J. Hum. Comput. Stud.
Jan L. Top, Hans Akkermans
1993 B conf
EKAW
Hans Akkermans, Bob J. Wielinga, Guus Schreiber
1993 conf
EUROVAV
Bob J. Wielinga, Hans Akkermans, Guus Schreiber
1992 B conf
EKAW
Guus Schreiber, Bob J. Wielinga, Hans Akkermans
1992 J jnl
AI Commun.
John Balder, Hans Akkermans
1991 J jnl
AI Commun.
Martin Reinders, Erik Vinkhuyzen, Angi Voß, Hans Akkermans, John Balder, Brigitte Bartsch-Spörl, Bert Bredeweg, Uwe Drouven, Frank van Harmelen, Werner Karbach, Zeger Karssen, Guus Schreiber, Bob J. Wielinga
1991 A* conf
IJCAI
Jan L. Top, Hans Akkermans
1990 A conf
ECAI
Jan L. Top, Hans Akkermans
tests/unit/test_decompile_binja_extractor.py
← Index tests/unit/test_decompile_binja_extractor.py python
"""Unit tests for DecompileBinja (top-level extractor) and prepare_export_data."""
import hashlib
import json
import os
import sys
import pytest
from unittest.mock import MagicMock, patch, PropertyMock
from datetime import datetime

# Install binaryninja stubs
from tests.unit.conftest_binja_stubs import install_binja_stubs
install_binja_stubs()

from redb.extractors.enum import Tag


# ============================================================================
# 7a. DecompileBinja — methods that don't require a live Binary Ninja session
# ============================================================================


class TestDecompileBinjaCalculateMD5:
    def test_calculate_md5(self):
        """calculate_md5("test") returns expected MD5."""
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = object.__new__(DecompileBinja)
            obj.log = MagicMock()
            result = obj.calculate_md5("test")
            expected = hashlib.md5(b"test").hexdigest()
            assert result == expected


class TestDecompileBinjaTimeouts:
    def _make_extractor(self, env_vars=None):
        """Create a DecompileBinja with mocked Extractor.__init__."""
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            if env_vars:
                with patch.dict(os.environ, env_vars, clear=False):
                    obj = DecompileBinja.__new__(DecompileBinja)
                    obj.log = MagicMock()
                    obj.analysis_results = None
                    obj.binja_decompiler = None
                    obj.filetype = None
                    obj.goresym_data = None
                    obj.goresym_output_path = None
                    # Re-run timeout logic
                    try:
                        obj.BINJA_TIMEOUT = int(os.getenv("BINJA_TIMEOUT", "1200"))
                    except ValueError:
                        obj.BINJA_TIMEOUT = 1200
                    try:
                        obj.DECOMPILE_EXTRACTOR_TIMEOUT = int(
                            os.getenv("DECOMPILE_EXTRACTOR_TIMEOUT", "2580")
                        )
                    except ValueError:
                        obj.DECOMPILE_EXTRACTOR_TIMEOUT = 2580
                    return obj
            else:
                obj = DecompileBinja.__new__(DecompileBinja)
                obj.log = MagicMock()
                obj.analysis_results = None
                obj.binja_decompiler = None
                obj.filetype = None
                obj.goresym_data = None
                obj.goresym_output_path = None
                obj.BINJA_TIMEOUT = 1200
                obj.DECOMPILE_EXTRACTOR_TIMEOUT = 2580
                return obj

    def test_timeout_defaults(self):
        obj = self._make_extractor()
        assert obj.BINJA_TIMEOUT == 1200
        assert obj.DECOMPILE_EXTRACTOR_TIMEOUT == 2580

    def test_timeout_from_env(self):
        obj = self._make_extractor(env_vars={"BINJA_TIMEOUT": "600"})
        assert obj.BINJA_TIMEOUT == 600

    def test_timeout_invalid_env(self):
        obj = self._make_extractor(env_vars={"BINJA_TIMEOUT": "not_a_number"})
        assert obj.BINJA_TIMEOUT == 1200


class TestDecompileBinjaDotnet:
    def _make_extractor(self, filetype=None):
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            obj.filetype = filetype
            obj.filepath = "/tmp/fake.bin"
            obj.binary = b"\x00" * 100
            obj.hash = MagicMock()
            obj.hash.sha256 = "a" * 64
            return obj

    def test_is_dotnet_non_pe(self):
        obj = self._make_extractor(filetype="elf")
        assert obj.is_dotnet() is False

    def test_is_dotnet_pe_without_com(self):
        """PE without .NET COM descriptor -> False."""
        obj = self._make_extractor(filetype="pebin")
        # Mock magic to not have .Net
        with patch("redb.extractors.decompiler.DecompileBinja.magic") as mock_magic:
            mock_magic.from_buffer.return_value = "PE32 executable"
            with patch("redb.extractors.decompiler.DecompileBinja.pefile") as mock_pefile:
                mock_pe = MagicMock()
                entry = MagicMock()
                entry.name = "IMAGE_DIRECTORY_ENTRY_IMPORT"
                entry.Size = 100
                mock_pe.OPTIONAL_HEADER.DATA_DIRECTORY = [entry]
                mock_pefile.PE.return_value = mock_pe
                assert obj.is_dotnet() is False

    def test_is_dotnet_pe_with_com_descriptor(self):
        """PE with COM descriptor -> True."""
        obj = self._make_extractor(filetype="pebin")
        with patch("redb.extractors.decompiler.DecompileBinja.magic") as mock_magic:
            mock_magic.from_buffer.return_value = "PE32 executable"
            with patch("redb.extractors.decompiler.DecompileBinja.pefile") as mock_pefile:
                mock_pe = MagicMock()
                entry = MagicMock()
                entry.name = "IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR"
                entry.Size = 72
                mock_pe.OPTIONAL_HEADER.DATA_DIRECTORY = [entry]
                mock_pefile.PE.return_value = mock_pe
                assert obj.is_dotnet() is True


class TestDecompileBinjaGolang:
    def _make_extractor(self, filetype=None):
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            obj.filetype = filetype
            obj.filepath = "/tmp/fake.bin"
            obj.hash = MagicMock()
            obj.hash.sha256 = "a" * 64
            return obj

    def test_is_golang_non_elf_pe(self):
        obj = self._make_extractor(filetype=None)
        assert obj.is_golang() is False

    def test_is_golang_elf_with_go_sections(self):
        """ELF with .gopclntab section -> True."""
        obj = self._make_extractor(filetype="elf")
        with patch("builtins.open", MagicMock()):
            with patch("redb.extractors.decompiler.DecompileBinja.ELFFile") as mock_elf_cls:
                mock_elf = MagicMock()
                sec1 = MagicMock()
                sec1.name = ".text"
                sec1.__getitem__ = lambda self, k: 0x2 if k == "sh_flags" else 0
                sec1.data.return_value = b""
                sec2 = MagicMock()
                sec2.name = ".gopclntab"
                sec2.__getitem__ = lambda self, k: 0x2 if k == "sh_flags" else 0
                sec2.data.return_value = b""
                mock_elf.iter_sections.return_value = [sec1, sec2]
                mock_elf_cls.return_value = mock_elf
                assert obj.is_golang() is True

    def test_is_golang_elf_without_go(self):
        obj = self._make_extractor(filetype="elf")
        with patch("builtins.open", MagicMock()):
            with patch("redb.extractors.decompiler.DecompileBinja.ELFFile") as mock_elf_cls:
                mock_elf = MagicMock()
                sec = MagicMock()
                sec.name = ".text"
                sec.__getitem__ = lambda self, k: 0x2 if k == "sh_flags" else 0
                sec.data.return_value = b"no go signatures here"
                mock_elf.iter_sections.return_value = [sec]
                mock_elf_cls.return_value = mock_elf
                assert obj.is_golang() is False


class TestDecompileBinjaTag:
    def test_tag_returns_decompiled(self):
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            assert obj.tag() == Tag.DECOMPILED.value


class TestDecompileBinjaContextManager:
    def test_context_manager_protocol(self):
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            obj.binja_decompiler = None
            obj.goresym_output_path = None
            assert obj.__enter__() is obj
            # __exit__ calls cleanup_run
            obj.__exit__(None, None, None)

    def test_cleanup_run_clears_state(self):
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            obj.binja_decompiler = MagicMock()
            obj.goresym_output_path = None
            obj.cleanup_run()
            assert obj.binja_decompiler is None


class TestDecompileBinjaExtract:
    def test_extract_timeout_handling(self):
        """Thread timeout -> returns None and calls cleanup."""
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            obj.analysis_results = None
            obj.binja_decompiler = None
            obj.goresym_output_path = None
            obj.DECOMPILE_EXTRACTOR_TIMEOUT = 0  # Immediate timeout
            obj.sha256 = "a" * 64
            obj.sha1 = "b" * 40
            obj.md5 = "c" * 32
            # Mock analyze_binary to block
            import threading
            obj.analyze_binary = lambda: None
            result = obj.extract()
            assert result is None

    def test_analyze_binary_skips_dotnet(self):
        """Dotnet binary -> analyze_binary returns None."""
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            obj.filetype = "pebin"
            obj.filepath = "/fake"
            obj.hash = MagicMock()
            obj.hash.sha256 = "a" * 64
            obj.binja_decompiler = None
            obj.goresym_output_path = None
            obj.goresym_data = None
            obj.BINJA_TIMEOUT = 60
            obj.exporters = []
            obj.index_prefix = None
            with patch.object(obj, "is_dotnet", return_value=True):
                result = obj.analyze_binary()
                assert result is None


# ============================================================================
# 7b. prepare_export_data (ClickHouse schema)
# ============================================================================


class TestPrepareExportData:
    def _make_extractor_with_results(self, goresym_data=None):
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            obj.goresym_data = goresym_data
            obj.analysis_results = {
                "sha256": "a" * 64,
                "sha1": "b" * 40,
                "md5": "c" * 32,
                "decompiled": [
                    {
                        "decompiled_function_hash": "h" * 64,
                        "decompiled_function": "void test() {}",
                        "decompiled_function_name": "test",
                        "decompiled_function_prototype": "void test()",
                        "decompiled_function_address": 0x1000,
                        "function_type": "USER",
                        "functions_caller": [],
                        "functions_call": [],
                        "disassembled_function_hash": "d" * 64,
                        "flattened_score": 0.0,
                        "mba_score": 0.0,
                    }
                ],
                "disassembled": [
                    {
                        "disassembled_function_hash": "d" * 64,
                        "disassembled_function": "0x1000: push rbp",
                        "disassembled_function_no_addresses": "push rbp",
                        "disassembled_function_name": "test",
                        "disassembled_function_address": 0x1000,
                        "function_type": "USER",
                        "instructions_count": 5,
                        "instructions_types": ["DATA_MOVEMENT"],
                        "control_flow_count": 1,
                        "memory_access_pattern": ["MEM_STACK"],
                        "register_usage": ["GPR"],
                        "data_references_count": 0,
                        "max_block_size": 5,
                        "num_calls": 1,
                        "stack_size": -8,
                        "decompiled_function_hash": "h" * 64,
                        "tlsh_disassembly": None,
                        "tlsh_llil": None,
                        "minhash": [1, 2, 3],
                        "cyclomatic_complexity": 2,
                    }
                ],
                "cfg": [
                    {
                        "function_address": 0x1000,
                        "disassembled_function_hash": "d" * 64,
                        "cfg_topology_hash": b'\x00' * 16,
                        "block_count": 3,
                        "edge_count": 4,
                        "llil_total_operations": 20,
                        "call_count": 1,
                        "cyclomatic_complexity": 3,
                        "loop_count": 1,
                        "max_depth": 2,
                        "max_fan_out": 2,
                        "md_index_topdown": 12345,
                        "md_index_bottomup": 67890,
                        "prime_product_llil": 99999,
                        "cfg_feature_tlsh": None,
                        "wl_minhash": [0] * 128,
                        "bb_features": [[5, 1, 0, 2, 0, 1, 1, 2]] * 3,
                        "cfg_adjacency": [(0 << 16) | 1, (0 << 16) | 2, (1 << 16) | 2],
                    }
                ],
                "llil": [
                    {
                        "sha256_llil": "l" * 64,
                        "function_type": "USER",
                        "instructions_types_llil": [],
                        "control_flow_count_llil": 0,
                        "memory_access_pattern_llil": [],
                        "register_usage": {},
                        "total_reg_reads": 0,
                        "total_reg_written": 0,
                        "data_references_count": 0,
                        "max_block_size": 5,
                        "num_calls": 0,
                        "stack_size": -8,
                        "body_llil_vector": [],
                        "function_address": 0x1000,
                        "disassembled_function_hash": "d" * 64,
                        "tlsh_disassembly": None,
                        "tlsh_llil": None,
                    }
                ],
                "errors": [],
                "strings": [
                    {
                        "string": "Hello",
                        "string_raw": "Hello",
                        "string_encoding": "Utf8String",
                        "string_offset": 0,
                        "string_length": 5,
                        "string_raw_length": 5,
                        "string_entropy": 2.32,
                    }
                ],
            }
            return obj

    def test_prepare_export_returns_none_no_results(self):
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            obj.analysis_results = None
            result = obj.prepare_export_data("ClickHouseExporter")
            assert result is None

    def test_prepare_export_multi_table_flag(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        assert result["multi_table"] is True

    def test_prepare_export_all_tables_present(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        expected_tables = [
            "decompiled_content",
            "decompiled_refs",
            "disassembled_content",
            "disassembled_refs",
            "llil_content",
            "llil_refs",
            "cfg_functions",
            "function_similarity_metrics",
            "strings_raw",
        ]
        for table in expected_tables:
            assert table in result, f"Missing table: {table}"

    def test_prepare_export_decompiled_content_schema(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        table = result["decompiled_content"]
        assert len(table["column_names"]) == len(table["column_type_names"])
        assert "decompiled_function_hash" in table["column_names"]

    def test_prepare_export_decompiled_refs_schema(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        table = result["decompiled_refs"]
        assert len(table["column_names"]) == len(table["column_type_names"])
        assert "sha256" in table["column_names"]

    def test_prepare_export_disassembled_content_schema(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        table = result["disassembled_content"]
        assert len(table["column_names"]) == len(table["column_type_names"])

    def test_prepare_export_disassembled_refs_schema(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        table = result["disassembled_refs"]
        assert len(table["column_names"]) == len(table["column_type_names"])

    def test_prepare_export_llil_content_schema(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        table = result["llil_content"]
        assert len(table["column_names"]) == len(table["column_type_names"])

    def test_prepare_export_llil_refs_schema(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        table = result["llil_refs"]
        assert len(table["column_names"]) == len(table["column_type_names"])

    def test_prepare_export_cfg_functions_schema(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        table = result["cfg_functions"]
        assert len(table["column_names"]) == len(table["column_type_names"])
        assert table["table"] == "code_binja_cfg_functions"
        assert "cfg_topology_hash" in table["column_names"]
        assert "wl_minhash" in table["column_names"]
        assert "bb_features" in table["column_names"]

    def test_prepare_export_function_similarity_schema(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        table = result["function_similarity_metrics"]
        assert len(table["column_names"]) == len(table["column_type_names"])

    def test_prepare_export_strings_raw_schema(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        table = result["strings_raw"]
        assert len(table["column_names"]) == len(table["column_type_names"])

    def test_prepare_export_errors_schema(self):
        obj = self._make_extractor_with_results()
        result = obj.prepare_export_data("ClickHouseExporter")
        table = result["function_analysis_errors"]
        assert len(table["column_names"]) == len(table["column_type_names"])

    def test_prepare_export_golang_metadata_present(self):
        obj = self._make_extractor_with_results(goresym_data={"UserFunctions": []})
        result = obj.prepare_export_data("ClickHouseExporter")
        assert "golang_metadata" in result

    def test_prepare_export_golang_metadata_absent(self):
        obj = self._make_extractor_with_results(goresym_data=None)
        result = obj.prepare_export_data("ClickHouseExporter")
        assert "golang_metadata" not in result


class TestPrepareHelperFunctions:
    """Test the helper functions inside prepare_export_data."""

    def test_prepare_array_field_none(self):
        # The prepare_array_field function converts None -> []
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            obj.goresym_data = None
            obj.analysis_results = {
                "sha256": "a" * 64, "sha1": "b" * 40, "md5": "c" * 32,
                "decompiled": [], "disassembled": [], "cfg": [], "llil": [],
                "errors": [], "strings": [],
            }
            result = obj.prepare_export_data("ClickHouseExporter")
            # Verify the export succeeds (function works even with empty lists)
            assert result is not None

    def test_prepare_array_field_list(self):
        """List values pass through."""
        # Verified through the export data containing arrays
        pass

    def test_prepare_register_usage_map(self):
        """Dict converted to {reg: (reads, writes)} tuples."""
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            obj.goresym_data = None
            obj.analysis_results = {
                "sha256": "a" * 64, "sha1": "b" * 40, "md5": "c" * 32,
                "decompiled": [], "disassembled": [],
                "cfg": [],
                "llil": [{
                    "sha256_llil": "l" * 64, "function_type": "USER",
                    "instructions_types_llil": [], "control_flow_count_llil": 0,
                    "memory_access_pattern_llil": [],
                    "register_usage": {"rbx": {"reads": 3, "writes": 1}},
                    "total_reg_reads": 3, "total_reg_written": 1,
                    "data_references_count": 0, "max_block_size": 5,
                    "num_calls": 0, "stack_size": 0,
                    "body_llil_vector": [],
                    "function_address": 0x1000,
                    "disassembled_function_hash": "d" * 64,
                    "tlsh_disassembly": None,
                    "tlsh_llil": None,
                }],
                "errors": [], "strings": [],
            }
            result = obj.prepare_export_data("ClickHouseExporter")
            # The LLIL content data should have register_usage_llil as a map
            llil_data = result["llil_content"]["data"]
            assert len(llil_data) == 1
            # Column index 5 is register_usage_llil
            reg_map = llil_data[0][5]
            assert reg_map == {"rbx": (3, 1)}

    def test_prepare_register_usage_map_empty(self):
        """Empty/None register usage -> {}."""
        with patch("redb.extractors.decompiler.DecompileBinja.Extractor.__init__", return_value=None):
            from redb.extractors.decompiler.DecompileBinja import DecompileBinja
            obj = DecompileBinja.__new__(DecompileBinja)
            obj.log = MagicMock()
            obj.goresym_data = None
            obj.analysis_results = {
                "sha256": "a" * 64, "sha1": "b" * 40, "md5": "c" * 32,
                "decompiled": [], "disassembled": [],
                "cfg": [],
                "llil": [{
                    "sha256_llil": "l" * 64, "function_type": "USER",
                    "instructions_types_llil": [], "control_flow_count_llil": 0,
                    "memory_access_pattern_llil": [],
                    "register_usage": None,
                    "total_reg_reads": 0, "total_reg_written": 0,
                    "data_references_count": 0, "max_block_size": 0,
                    "num_calls": 0, "stack_size": 0,
                    "body_llil_vector": [],
                    "function_address": 0x1000,
                    "disassembled_function_hash": "d" * 64,
                    "tlsh_disassembly": None,
                    "tlsh_llil": None,
                }],
                "errors": [], "strings": [],
            }
            result = obj.prepare_export_data("ClickHouseExporter")
            llil_data = result["llil_content"]["data"]
            reg_map = llil_data[0][5]
            assert reg_map == {}