Carroll Morgan

117 papers A* 5A 3B 10C 6Journal 55Unranked 30
YearRankTypeTitle / Venue / Authors
2025 C conf
ICTAC
Chris Chen, Annabelle McIver, Carroll Morgan
2025 J jnl
CoRR
Chris Chen, Annabelle McIver, Carroll Morgan
2025 J jnl
Formal Aspects Comput.
Brijesh Dongol, Catherine Dubois, Stefan Hallerstede, Eric C. R. Hehner, Carroll Morgan, Peter Müller, Leila Ribeiro, Alexandra Silva, Graeme Smith, Erik P. de Vink
2024 C conf
ICTAC
Chris Chen, Annabelle McIver, Carroll Morgan
2024 conf
Principles of Verification (1)
Chris Chen, Annabelle McIver, Carroll Morgan
2024 J jnl
CoRR
Chris Chen, Annabelle McIver, Carroll Morgan
2023 A* conf
CCS
Mário S. Alvim, Natasha Fernandes, Annabelle McIver, Carroll Morgan, Gabriel Henrique Nunes
2023 J jnl
CoRR
Mário S. Alvim, Natasha Fernandes, Annabelle McIver, Carroll Morgan, Gabriel Henrique Nunes
2022 J jnl
CoRR
Mário S. Alvim, Natasha Fernandes, Annabelle McIver, Carroll Morgan, Gabriel Henrique Nunes
2022 J jnl
Proc. Priv. Enhancing Technol.
Mário S. Alvim, Natasha Fernandes, Annabelle McIver, Carroll Morgan, Gabriel Henrique Nunes
2022 B conf
CSL
Natasha Fernandes, Annabelle McIver, Carroll Morgan
2021 ch.
IFIP's Exciting First 60+ Years
Richard S. Bird, Jeremy Gibbons, Ralf Hinze, Peter Höfner, Johan Jeuring, Lambert G. L. T. Meertens, Bernhard Möller, Carroll Morgan, Tom Schrijvers, Wouter Swierstra, Nicolas Wu
2021 A* conf
LICS
Natasha Fernandes, Annabelle McIver, Carroll Morgan
2021 J jnl
CoRR
Natasha Fernandes, Annabelle McIver, Carroll Morgan
2020 conf
ISoLA (1)
Annabelle McIver, Carroll Morgan
2020 J jnl
CoRR
Annabelle McIver, Carroll Morgan
2020 J jnl
Acta Informatica
Peter Höfner, Carroll Morgan, Vaughan R. Pratt
2020 ch.
Foundations of Probabilistic Programming
Jeremy Gibbons, Annabelle McIver, Carroll Morgan, Tom Schrijvers
2020 book
Mário S. Alvim, Konstantinos Chatzikokolakis, Annabelle McIver, Carroll Morgan, Catuscia Palamidessi, Geoffrey Smith
2019 J jnl
Log. Methods Comput. Sci.
Annabelle McIver, Carroll Morgan, Tahiry M. Rabehaja
2019 J jnl
Theor. Comput. Sci.
Mário S. Alvim, Konstantinos Chatzikokolakis, Annabelle McIver, Carroll Morgan, Catuscia Palamidessi, Geoffrey Smith
2019 conf
The Art of Modelling Computational Systems
Tahiry M. Rabehaja, Annabelle McIver, Carroll Morgan, Georg Struth
2019 B conf
APLAS
Annabelle McIver, Carroll Morgan
2018 J jnl
Proc. ACM Program. Lang.
Annabelle McIver, Carroll Morgan, Benjamin Lucien Kaminski, Joost-Pieter Katoen
2017 conf
Concurrency, Security, and Puzzles
Carroll Morgan
2017 J jnl
CoRR
Annabelle McIver, Carroll Morgan, Benjamin Lucien Kaminski, Joost-Pieter Katoen
2017 J jnl
CoRR
Annabelle McIver, Carroll Morgan, Tahiry M. Rabehaja
2017 J jnl
J. Inf. Secur. Appl.
Annabelle McIver, Tahiry M. Rabehaja, Roland Wen, Carroll Morgan
2017 C conf
FORTE
Nicolás E. Bordenabe, Annabelle McIver, Carroll Morgan, Tahiry M. Rabehaja
2016 J jnl
CoRR
Annabelle McIver, Carroll Morgan
2016 A conf
CSF
Mário S. Alvim, Konstantinos Chatzikokolakis, Annabelle McIver, Carroll Morgan, Catuscia Palamidessi, Geoffrey Smith
2016 J jnl
CoRR
Nicolás E. Bordenabe, Annabelle McIver, Carroll Morgan, Tahiry M. Rabehaja
2016 B conf
ITP
June Andronick, Corey Lewis, Daniel Matichuk, Carroll Morgan, Christine Rizkallah
2015 A* conf
LICS
Annabelle McIver, Carroll Morgan, Tahiry M. Rabehaja
2015 conf
MARS
June Andronick, Corey Lewis, Carroll Morgan
2015 J jnl
Math. Struct. Comput. Sci.
Annabelle McIver, Larissa Meinicke, Carroll Morgan
2014 conf
SETSS
Carroll Morgan
2014 conf
POST
Annabelle McIver, Carroll Morgan, Geoffrey Smith, Barbara Espinoza, Larissa Meinicke
2014 A conf
CSF
Mário S. Alvim, Konstantinos Chatzikokolakis, Annabelle McIver, Carroll Morgan, Catuscia Palamidessi, Geoffrey Smith
2014 J jnl
Sci. Comput. Program.
Carroll Morgan
2014 J jnl
Theor. Comput. Sci.
Yuxin Deng, Rob J. van Glabbeek, Matthew Hennessy, Carroll Morgan
2014 J jnl
Sci. Comput. Program.
Adenilso da Silva Simão, Carroll Morgan
2014 B conf
FM
Roland Wen, Annabelle McIver, Carroll Morgan
2012 A* conf
LICS
Annabelle McIver, Larissa Meinicke, Carroll Morgan
2012 J jnl
Formal Aspects Comput.
Carroll Morgan
2012 B conf
MPC
Carroll Morgan
2012 J jnl
NII Shonan Meet. Rep.
Jin Song Dong, Annabelle McIver, Carroll Morgan
2011 ed.
SBMF
Adenilso da Silva Simão, Carroll Morgan
2011 J jnl
CoRR
Annabelle McIver, Larissa Meinicke, Carroll Morgan
2011 conf
QAPL
Yuxin Deng, Rob J. van Glabbeek, Matthew Hennessy, Carroll Morgan
2010 conf
ICALP (2)
Annabelle McIver, Larissa Meinicke, Carroll Morgan
2010 J jnl
CoRR
Annabelle McIver, Larissa Meinicke, Carroll Morgan
2009 conf
REFINE@FMWeek
Carroll Morgan
2009 ed.
QFM
Suzana Andova, Annabelle McIver, Pedro R. D'Argenio, Pieter J. L. Cuijpers, Jasen Markovski, Carroll Morgan, Manuel Núñez
2009 B conf
FM
Annabelle McIver, Larissa Meinicke, Carroll Morgan
2009 B conf
CONCUR
Yuxin Deng, Rob J. van Glabbeek, Matthew Hennessy, Carroll Morgan
2009 J jnl
Sci. Comput. Program.
Carroll Morgan
2009 C ed.
ICTAC
Martin Leucker, Carroll Morgan
2008 J jnl
CoRR
Yuxin Deng, Matthew Hennessy, Rob J. van Glabbeek, Carroll Morgan
2008 J jnl
Log. Methods Comput. Sci.
Yuxin Deng, Rob J. van Glabbeek, Matthew Hennessy, Carroll Morgan
2007 A* conf
LICS
Yuxin Deng, Rob J. van Glabbeek, Matthew Hennessy, Carroll Morgan, Chenyi Zhang
2007 conf
Computation, Meaning, and Logic
Yuxin Deng, Rob J. van Glabbeek, Matthew Hennessy, Carroll Morgan, Chenyi Zhang
2007 J jnl
ACM Trans. Comput. Log.
Annabelle McIver, Carroll Morgan
2007 A conf
ESOP
Yuxin Deng, Rob J. van Glabbeek, Carroll Morgan, Chenyi Zhang
2006 conf
RODIN Book
Carroll Morgan, Annabelle McIver
2006 conf
RelMiCS
Annabelle McIver, Ernie Cohen, Carroll Morgan
2005 conf
QAPL
Annabelle McIver, Carroll Morgan
2005 J jnl
SIGMETRICS Perform. Evaluation Rev.
Annabelle McIver, Carroll Morgan
2005 book
Annabelle McIver, Carroll Morgan
2005 J jnl
Inf. Process. Lett.
Annabelle McIver, Carroll Morgan
2005 conf
ZB
Thai Son Hoang, Zhendong Jin, Ken Robinson, Annabelle McIver, Carroll Morgan
2005 J jnl
Theor. Comput. Sci.
Joe Hurd, Annabelle McIver, Carroll Morgan
2005 conf
ZB
Carroll Morgan, Thai Son Hoang, Jean-Raymond Abrial
2004 C conf
ICFEM
Jifeng He, Carroll Morgan, Annabelle McIver
2004 conf
PSSE
Annabelle McIver, Carroll Morgan
2004 conf
SBMF
Carroll Morgan, Annabelle McIver
2004 conf
25 Years Communicating Sequential Processes
Carroll Morgan
2004 conf
QAPL
Joe Hurd, Annabelle McIver, Carroll Morgan
2003 J jnl
Theor. Comput. Sci.
Annabelle McIver, Carroll Morgan
2003 conf
ZB
Thai Son Hoang, Zhendong Jin, Ken Robinson, Annabelle McIver, Carroll Morgan
2003 conf
ZB
Annabelle McIver, Carroll Morgan, Thai Son Hoang
2003 J jnl
CoRR
Annabelle McIver, Carroll Morgan
2002 B conf
LPAR
Annabelle McIver, Carroll Morgan
2001 conf
CATS
Annabelle McIver, Carroll Morgan
2001 C conf
APSEC
Carroll Morgan, Annabelle McIver
2001 J jnl
Acta Informatica
Annabelle McIver, Carroll Morgan
2001 J jnl
Theor. Comput. Sci.
Annabelle McIver, Carroll Morgan
2000 conf
IWFM
Steve King, Carroll Morgan
1999 J jnl
Log. J. IGPL
Carroll Morgan, Annabelle McIver
1998 conf
B
Carroll Morgan
1996 J jnl
ACM Trans. Program. Lang. Syst.
Carroll Morgan, Annabelle McIver, Karen Seidel
1996 J jnl
Formal Aspects Comput.
Carroll Morgan, Annabelle McIver, Karen Seidel, Jeff W. Sanders
1996 J jnl
Inf. Process. Lett.
Carroll Morgan, Annabelle McIver
1995 J jnl
Formal Aspects Comput.
Michael J. Butler, Carroll Morgan
1995 J jnl
Formal Aspects Comput.
Steve King, Carroll Morgan
1994 J jnl
Sci. Comput. Program.
Carroll Morgan
1994 book
Programming from specifications, 2nd Edition.
Carroll Morgan
1993 J jnl
Formal Aspects Comput.
Paul H. B. Gardiner, Carroll Morgan
1993 B ed.
MPC
Richard S. Bird, Carroll Morgan, Jim Woodcock
1993 conf
Formal Program Development
Carroll Morgan
1992 conf
NATO ASI PDC
Carroll Morgan
1991 J jnl
Theor. Comput. Sci.
Paul H. B. Gardiner, Carroll Morgan
1990 J jnl
Acta Informatica
Carroll Morgan, Paul H. B. Gardiner
1990 book
Programming from specifications.
Carroll Morgan
1990 conf
VDM Europe
Jim Woodcock, Carroll Morgan
1990 J jnl
Sci. Comput. Program.
Carroll Morgan, Trevor Vickers
1989 B conf
MPC
Carroll Morgan
1988 J jnl
Inf. Process. Lett.
Carroll Morgan
1988 J jnl
Inf. Process. Lett.
Carroll Morgan
1988 J jnl
Sci. Comput. Program.
Carroll Morgan
1988 J jnl
ACM Trans. Program. Lang. Syst.
Carroll Morgan
1987 J jnl
Commun. ACM
C. A. R. Hoare, Ian J. Hayes, Jifeng He, Carroll Morgan, A. W. Roscoe, Jeff W. Sanders, Ib Holm Sørensen, J. Michael Spivey, Bernard Sufrin
1985 J jnl
Inf. Process. Lett.
Carroll Morgan
1984 J jnl
IEEE Trans. Software Eng.
Carroll Morgan, Bernard Sufrin
1983 conf
The Analysis of Concurrent Systems
Carroll Morgan, C. A. R. Hoare
1983 conf
The Analysis of Concurrent Systems
Carroll Morgan
1976 J jnl
SIGACT News
Carroll Morgan
tests/integration/test_basicproperties.py
← Index tests/integration/test_basicproperties.py python
"""
Integration tests for the BasicPropertiesExtractor class.
"""
import pytest
import os
from unittest.mock import Mock, patch, MagicMock

pytestmark = [pytest.mark.integration]


class TestBasicPropertiesExtractorInitialization:
    """Tests for BasicPropertiesExtractor initialization."""

    def test_initialization(self, elf_binary_path, mock_logger):
        """Test basic initialization."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(elf_binary_path, mock_logger)

            assert extractor.filepath == elf_binary_path
            assert extractor.basic_properties is None
            assert extractor.is_packed is None
            assert extractor.is_fat is None
            assert extractor.parent_sha256 is None
            assert extractor.child_sha256 is None
            assert extractor.child_architecture is None
            assert extractor.child_filetype is None

    def test_initialization_with_fat_params(self, elf_binary_path, mock_logger):
        """Test initialization with FAT Mach-O parameters."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            child_hashes = ["abc123", "def456"]
            child_archs = ["x86_64", "arm64"]
            child_types = ["macho", "macho"]

            extractor = BasicPropertiesExtractor(
                elf_binary_path,
                mock_logger,
                is_fat=True,
                child_sha256=child_hashes,
                child_architecture=child_archs,
                child_filetype=child_types
            )

            assert extractor.is_fat is True
            assert extractor.child_sha256 == child_hashes
            assert extractor.child_architecture == child_archs
            assert extractor.child_filetype == child_types

    def test_initialization_with_parent_sha256(self, elf_binary_path, mock_logger):
        """Test initialization with parent_sha256 for slice binaries."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            parent_hash = "parent_sha256_hash_here"

            extractor = BasicPropertiesExtractor(
                elf_binary_path,
                mock_logger,
                parent_sha256=parent_hash
            )

            assert extractor.parent_sha256 == parent_hash

    def test_elastic_index_suffix(self, elf_binary_path, mock_logger):
        """Test that elastic_index has correct suffix."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(elf_binary_path, mock_logger)

            assert "-basic_properties" in extractor.elastic_index


class TestBasicPropertiesExtractorTag:
    """Tests for tag method."""

    def test_tag_value(self, elf_binary_path, mock_logger):
        """Test that tag returns correct value."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor
        from redb.extractors.enum import Tag

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(elf_binary_path, mock_logger)

            assert extractor.tag() == Tag.BASIC_PROPERTIES.value
            assert extractor.tag() == "basic_properties"


class TestBasicPropertiesExtract:
    """Tests for extract method."""

    def test_extract_elf_binary(self, elf_binary_path, mock_logger):
        """Test extracting properties from ELF binary."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor
        from redb.models.dataclasses import BasicProperties

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            assert isinstance(result, BasicProperties)
            assert result.sample_name == os.path.basename(elf_binary_path)
            assert result.filesize > 0
            assert result.file_entropy >= 0
            assert result.filetype is not None
            assert result.filetype_mime is not None
            assert result.filetype_magika is not None

    def test_extract_pe_binary(self, pe_binary_path, mock_logger):
        """Test extracting properties from PE binary."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor
        from redb.models.dataclasses import BasicProperties

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(pe_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            assert isinstance(result, BasicProperties)
            assert result.filesize > 0
            # PE file should be detected
            assert "PE" in result.filetype or "executable" in result.filetype.lower()

    def test_extract_text_file(self, temp_text_file, mock_logger):
        """Test extracting properties from text file."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(temp_text_file, mock_logger)

            result = extractor.extract()

            assert result is not None
            assert "text" in result.filetype.lower() or "ASCII" in result.filetype
            assert result.file_entropy >= 0

    def test_extract_stores_in_basic_properties(self, elf_binary_path, mock_logger):
        """Test that extract stores result in basic_properties attribute."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(elf_binary_path, mock_logger)

            assert extractor.basic_properties is None
            result = extractor.extract()
            assert extractor.basic_properties is result


class TestBasicPropertiesFiletype:
    """Tests for file type detection."""

    def test_elf_filetype(self, elf_binary_path, mock_logger):
        """Test ELF file type detection."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # Should detect as ELF
            assert "ELF" in result.filetype

    def test_pe_filetype_mime(self, pe_binary_path, mock_logger):
        """Test PE file type MIME detection."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(pe_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # PE MIME types (varies by libmagic version)
            assert "dosexec" in result.filetype_mime or "x-msdownload" in result.filetype_mime or "portable-executable" in result.filetype_mime

    def test_text_filetype_mime(self, temp_text_file, mock_logger):
        """Test text file type MIME detection."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(temp_text_file, mock_logger)

            result = extractor.extract()

            assert result is not None
            # Text MIME types
            assert "text" in result.filetype_mime


class TestBasicPropertiesEntropy:
    """Tests for entropy calculation in BasicPropertiesExtractor."""

    def test_entropy_reasonable_range(self, elf_binary_path, mock_logger):
        """Test that entropy is within reasonable range (0-8)."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            assert 0 <= result.file_entropy <= 8

    def test_entropy_rounded(self, elf_binary_path, mock_logger):
        """Test that entropy is rounded to 3 decimal places."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # Check rounding to 3 decimal places
            entropy_str = str(result.file_entropy)
            if '.' in entropy_str:
                decimal_places = len(entropy_str.split('.')[1])
                assert decimal_places <= 3


class TestBasicPropertiesFilesize:
    """Tests for file size calculation."""

    def test_filesize_matches_actual(self, elf_binary_path, mock_logger):
        """Test that filesize matches actual file size."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            actual_size = os.path.getsize(elf_binary_path)
            assert result.filesize == actual_size

    def test_filesize_matches_binary_length(self, elf_binary_path, mock_logger, elf_binary_content):
        """Test that filesize matches binary content length."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result.filesize == len(elf_binary_content)


class TestBasicPropertiesPrepareExportData:
    """Tests for prepare_export_data method."""

    def test_prepare_export_elasticsearch(self, elf_binary_path, mock_logger):
        """Test prepare_export_data for Elasticsearch."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(elf_binary_path, mock_logger)
            extractor.extract()

            result = extractor.prepare_export_data("ElasticsearchExporter")

            assert result is extractor.basic_properties

    def test_prepare_export_clickhouse(self, elf_binary_path, mock_logger):
        """Test prepare_export_data for ClickHouse."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(elf_binary_path, mock_logger)
            extractor.extract()

            result = extractor.prepare_export_data("ClickHouseExporter")

            # Should return tuple of (data, column_names, column_type_names)
            assert isinstance(result, tuple)
            assert len(result) == 3

            data, column_names, column_type_names = result

            assert isinstance(data, list)
            assert len(data) == 1  # One row
            assert isinstance(column_names, list)
            assert isinstance(column_type_names, list)

            # Check expected columns
            assert 'sha256' in column_names
            assert 'md5' in column_names
            assert 'sha1' in column_names
            assert 'filesize' in column_names
            assert 'file_entropy' in column_names
            assert 'filetype' in column_names
            assert 'filetype_mime' in column_names
            assert 'filetype_magika' in column_names
            # New FAT/child columns
            assert 'parent_sha256' in column_names
            assert 'child_sha256' in column_names
            assert 'child_architecture' in column_names
            assert 'child_filetype' in column_names
            assert 'is_fat' in column_names

    def test_prepare_export_clickhouse_data_types(self, elf_binary_path, mock_logger):
        """Test that ClickHouse export data has correct types."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(elf_binary_path, mock_logger)
            extractor.extract()

            data, column_names, column_type_names = extractor.prepare_export_data("ClickHouseExporter")

            row = data[0]

            # Check that sha256 is a string with correct length
            sha256_idx = column_names.index('sha256')
            assert isinstance(row[sha256_idx], str)
            assert len(row[sha256_idx]) == 64

            # Check that filesize is an integer
            filesize_idx = column_names.index('filesize')
            assert isinstance(row[filesize_idx], int)

            # Check that file_entropy is a float
            entropy_idx = column_names.index('file_entropy')
            assert isinstance(row[entropy_idx], float)


class TestBasicPropertiesClickHouseTable:
    """Tests for get_clickhouse_table method."""

    def test_clickhouse_table_name(self, elf_binary_path, mock_logger):
        """Test correct ClickHouse table name."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(elf_binary_path, mock_logger)

            assert extractor.get_clickhouse_table() == "redb_basic_properties"


class TestBasicPropertiesExportData:
    """Tests for export_data method."""

    def test_export_data_with_print_exporter(self, elf_binary_path, mock_logger, mock_print_exporter, capsys):
        """Test export_data with PrintExporter."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(
                elf_binary_path,
                mock_logger,
                exporters=[mock_print_exporter]
            )

            result = extractor.export_data()

            assert result is True

    def test_export_data_empty_exporters(self, elf_binary_path, mock_logger):
        """Test export_data with no exporters."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(
                elf_binary_path,
                mock_logger,
                exporters=[]
            )

            result = extractor.export_data()

            # Should succeed with no exporters
            assert result is True


class TestBasicPropertiesMagika:
    """Tests for Magika file type detection."""

    def test_magika_elf_detection(self, elf_binary_path, mock_logger):
        """Test Magika detection of ELF files."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # Magika should detect ELF
            assert result.filetype_magika == "elf" or "elf" in result.filetype_magika.lower()

    def test_magika_pe_detection(self, pe_binary_path, mock_logger):
        """Test Magika detection of PE files."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(pe_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # Magika should detect PE
            assert "pe" in result.filetype_magika.lower() or "exe" in result.filetype_magika.lower()


class TestBasicPropertiesFATSupport:
    """Tests for FAT Mach-O and child file support."""

    def test_extract_with_fat_flag(self, elf_binary_path, mock_logger):
        """Test extraction with is_fat flag set."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(
                elf_binary_path,
                mock_logger,
                is_fat=True
            )

            result = extractor.extract()

            assert result is not None
            assert result.is_fat is True

    def test_extract_with_child_hashes(self, elf_binary_path, mock_logger):
        """Test extraction with child_sha256 provided."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            child_hashes = ["abc123def456", "789ghi012jkl"]

            extractor = BasicPropertiesExtractor(
                elf_binary_path,
                mock_logger,
                child_sha256=child_hashes
            )

            result = extractor.extract()

            assert result is not None
            assert result.child_sha256 == child_hashes

    def test_extract_with_child_architecture(self, elf_binary_path, mock_logger):
        """Test extraction with child_architecture provided."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            child_archs = ["x86_64", "arm64"]

            extractor = BasicPropertiesExtractor(
                elf_binary_path,
                mock_logger,
                child_architecture=child_archs
            )

            result = extractor.extract()

            assert result is not None
            assert result.child_architecture == child_archs

    def test_extract_with_child_filetype(self, elf_binary_path, mock_logger):
        """Test extraction with child_filetype provided."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            child_types = ["macho", "macho"]

            extractor = BasicPropertiesExtractor(
                elf_binary_path,
                mock_logger,
                child_filetype=child_types
            )

            result = extractor.extract()

            assert result is not None
            assert result.child_filetype == child_types

    def test_clickhouse_export_with_fat_data(self, elf_binary_path, mock_logger):
        """Test ClickHouse export includes FAT-related fields correctly."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            child_hashes = ["abc123", "def456"]
            child_archs = ["x86_64", "arm64"]
            child_types = ["macho", "macho"]

            extractor = BasicPropertiesExtractor(
                elf_binary_path,
                mock_logger,
                is_fat=True,
                child_sha256=child_hashes,
                child_architecture=child_archs,
                child_filetype=child_types,
                parent_sha256=None  # FAT container has no parent
            )
            extractor.extract()

            data, column_names, column_type_names = extractor.prepare_export_data("ClickHouseExporter")
            row = data[0]

            # Check FAT fields
            is_fat_idx = column_names.index('is_fat')
            assert row[is_fat_idx] is True

            child_sha256_idx = column_names.index('child_sha256')
            assert row[child_sha256_idx] == child_hashes

            child_arch_idx = column_names.index('child_architecture')
            assert row[child_arch_idx] == child_archs

            child_type_idx = column_names.index('child_filetype')
            assert row[child_type_idx] == child_types

    def test_clickhouse_export_for_slice(self, elf_binary_path, mock_logger):
        """Test ClickHouse export for a slice binary with parent_sha256."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            parent_hash = "parent_container_sha256_hash"

            extractor = BasicPropertiesExtractor(
                elf_binary_path,
                mock_logger,
                parent_sha256=parent_hash
            )
            extractor.extract()

            data, column_names, column_type_names = extractor.prepare_export_data("ClickHouseExporter")
            row = data[0]

            parent_idx = column_names.index('parent_sha256')
            assert row[parent_idx] == parent_hash

    def test_default_fat_fields_are_none_or_empty(self, elf_binary_path, mock_logger):
        """Test that FAT-related fields default to None/empty for non-FAT binaries."""
        from redb.extractors.basicproperties import BasicPropertiesExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = BasicPropertiesExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            assert result.is_fat is None
            assert result.child_sha256 is None
            assert result.child_architecture is None
            assert result.child_filetype is None


@pytest.mark.unit
@pytest.mark.dataclass
class TestBasicPropertiesDataclass:
    """Tests for BasicProperties dataclass structure."""

    def test_dataclass_has_all_expected_fields(self):
        """Test that BasicProperties dataclass has all expected fields."""
        from redb.models.dataclasses import BasicProperties

        # Create a sample instance
        bp = BasicProperties(
            filename="test.exe",
            sample_name="test.exe",
            filesize=1024,
            filetype="PE32",
            filetype_mime="application/x-dosexec",
            filetype_magika="pebin",
            file_entropy=6.5
        )

        # Check required fields
        assert bp.filename == "test.exe"
        assert bp.sample_name == "test.exe"
        assert bp.filesize == 1024
        assert bp.filetype == "PE32"
        assert bp.filetype_mime == "application/x-dosexec"
        assert bp.filetype_magika == "pebin"
        assert bp.file_entropy == 6.5

        # Check optional fields default to None
        assert bp.is_packed is None
        assert bp.is_fat is None
        assert bp.child_sha256 is None
        assert bp.child_architecture is None
        assert bp.child_filetype is None

    def test_dataclass_with_optional_fat_fields(self):
        """Test BasicProperties dataclass with FAT-related optional fields."""
        from redb.models.dataclasses import BasicProperties

        bp = BasicProperties(
            filename="universal.app",
            sample_name="universal.app",
            filesize=2048,
            filetype="Mach-O universal",
            filetype_mime="application/x-mach-binary",
            filetype_magika="macho",
            file_entropy=7.2,
            is_fat=True,
            child_sha256=["hash1", "hash2"],
            child_architecture=["x86_64", "arm64"],
            child_filetype=["macho", "macho"]
        )

        assert bp.is_fat is True
        assert bp.child_sha256 == ["hash1", "hash2"]
        assert bp.child_architecture == ["x86_64", "arm64"]
        assert bp.child_filetype == ["macho", "macho"]