C.-H. Luke Ong

138 papers A* 32A 10B 18C 2Misc 1Journal 52Unranked 20
YearRankTypeTitle / Venue / Authors
2026 J jnl
CoRR
Haoxuan Yin, Andrzej S. Murawski, C.-H. Luke Ong
2026 J jnl
CoRR
Taoran Wu, Dominik Wagner, C.-H. Luke Ong, Bai Xue
2025 J jnl
Proc. ACM Program. Lang.
Fabian Zaiser, Andrzej S. Murawski, C.-H. Luke Ong
2025 J jnl
CoRR
Peixin Wang, Jianhao Bai, Min Zhang, C.-H. Luke Ong
2024 J jnl
CoRR
Dominik Wagner, Basim Khajwal, C.-H. Luke Ong
2024 J jnl
CoRR
Fabian Zaiser, Andrzej S. Murawski, C.-H. Luke Ong
2024 J jnl
Proc. ACM Program. Lang.
Peixin Wang, Tengshun Yang, Hongfei Fu, Guanyan Li, C.-H. Luke Ong
2024 conf
CAV (2)
Dapeng Zhi, Peixin Wang, Si Liu, C.-H. Luke Ong, Min Zhang
2023 A conf
ESOP
Basim Khajwal, C.-H. Luke Ong, Dominik Wagner
2023 J jnl
CoRR
Basim Khajwal, C.-H. Luke Ong, Dominik Wagner
2022 A* conf
PLDI
Eddie Jones, C.-H. Luke Ong, Steven J. Ramsay
2022 A* conf
PLDI
Raven Beutner, C.-H. Luke Ong, Fabian Zaiser
2021 J jnl
ACM Trans. Comput. Log.
Christopher H. Broadbent, Arnaud Carayol, Matthew Hague, Andrzej S. Murawski, C.-H. Luke Ong, Olivier Serre
2021 J jnl
CoRR
Eddie Jones, C.-H. Luke Ong, Steven J. Ramsay
2021 A conf
ESOP
Carol Mak, C.-H. Luke Ong, Hugo Paquet, Dominik Wagner
2021 J jnl
ACM Trans. Comput. Log.
Christopher H. Broadbent, Arnaud Carayol, C.-H. Luke Ong, Olivier Serre
2021 A* conf
LICS
Andrew Kenyon-Roberts, C.-H. Luke Ong
2020 J jnl
CoRR
Christopher H. Broadbent, Arnaud Carayol, Matthew Hague, Andrzej S. Murawski, C.-H. Luke Ong, Olivier Serre
2020 J jnl
CoRR
Carol Mak, C.-H. Luke Ong, Hugo Paquet, Dominik Wagner
2020 J jnl
CoRR
Christopher H. Broadbent, Arnaud Carayol, C.-H. Luke Ong, Olivier Serre
2020 J jnl
CoRR
Mario Alvarez-Picallo, C.-H. Luke Ong
2020 B conf
FSCD
Mario Alvarez-Picallo, C.-H. Luke Ong
2020 conf
VPT/HCVS@ETAPS
Fabian Zaiser, C.-H. Luke Ong
2020 J jnl
CoRR
Martin Lester, Robin P. Neatherway, C.-H. Luke Ong, Steven J. Ramsay
2019 B conf
FoSSaCS
Mario Alvarez-Picallo, C.-H. Luke Ong
2019 J jnl
CoRR
Mario Alvarez-Picallo, C.-H. Luke Ong
2019 A conf
ESOP
Mario Alvarez-Picallo, Alex Eyers-Taylor, Michael Peyton Jones, C.-H. Luke Ong
2019 A* conf
LICS
C.-H. Luke Ong, Dominik Wagner
2019 J jnl
CoRR
C.-H. Luke Ong, Dominik Wagner
2019 J jnl
ACM Trans. Program. Lang. Syst.
Conrad Cotton-Barratt, Andrzej S. Murawski, C.-H. Luke Ong
2019 J jnl
CoRR
Anne Schreuder, C.-H. Luke Ong
2019 B conf
JELIA
Rolf Morel, Andrew Cropper, C.-H. Luke Ong
2018 J jnl
CoRR
Long Pham, Steven J. Ramsay, C.-H. Luke Ong
2018 J jnl
CoRR
Mario Alvarez-Picallo, Alex Eyers-Taylor, Michael Peyton Jones, C.-H. Luke Ong
2018 J jnl
Proc. ACM Program. Lang.
Toby Cathcart Burn, C.-H. Luke Ong, Steven J. Ramsay
2018 conf
TACAS (2)
Zhao Duan, Cong Tian, Zhenhua Duan, C.-H. Luke Ong
2018 A* conf
LICS
Takeshi Tsukada, Kazuyuki Asada, C.-H. Luke Ong
2017 conf
ICLA
C.-H. Luke Ong
2017 J jnl
ACM Trans. Comput. Log.
Matthew Hague, Andrzej S. Murawski, C.-H. Luke Ong, Olivier Serre
2017 A* conf
LICS
Takeshi Tsukada, Kazuyuki Asada, C.-H. Luke Ong
2017 J jnl
CoRR
Toby Cathcart Burn, C.-H. Luke Ong, Steven J. Ramsay
2017 A conf
ESOP
Conrad Cotton-Barratt, Andrzej S. Murawski, C.-H. Luke Ong
2017 A* conf
ASE
Cong Tian, Zhao Duan, Zhenhua Duan, C.-H. Luke Ong
2017 A* conf
LICS
C.-H. Luke Ong
2016 A conf
ESOP
Emanuele D'Osualdo, C.-H. Luke Ong
2016 J jnl
CoRR
Emanuele D'Osualdo, C.-H. Luke Ong
2016 A* conf
LICS
Takeshi Tsukada, C.-H. Luke Ong
2016 A* conf
POPL
Matthew Hague, Jonathan Kochems, C.-H. Luke Ong
2015 A conf
OOPSLA
Matthew Hague, Anthony Widjaja Lin, C.-H. Luke Ong
2015 B conf
FoSSaCS
Conrad Cotton-Barratt, David Hopkins, Andrzej S. Murawski, C.-H. Luke Ong
2015 J jnl
CoRR
Conrad Cotton-Barratt, David Hopkins, Andrzej S. Murawski, C.-H. Luke Ong
2015 J jnl
Theor. Comput. Sci.
C.-H. Luke Ong, Ruy J. G. B. de Queiroz
2015 A* conf
LICS
Takeshi Tsukada, C.-H. Luke Ong
2015 J jnl
CoRR
C.-H. Luke Ong
2015 J jnl
CoRR
Matthew Hague, Jonathan Kochems, C.-H. Luke Ong
2015 C conf
LATA
Conrad Cotton-Barratt, Andrzej S. Murawski, C.-H. Luke Ong
2014 A* conf
POPL
Steven J. Ramsay, Robin P. Neatherway, C.-H. Luke Ong
2014 conf
CSL-LICS
Takeshi Tsukada, C.-H. Luke Ong
2014 J jnl
CoRR
Jonathan Kochems, C.-H. Luke Ong
2014 J jnl
CoRR
Takeshi Tsukada, C.-H. Luke Ong
2014 J jnl
CoRR
Jonathan Kochems, C.-H. Luke Ong
2014 conf
SPIN
Robin P. Neatherway, C.-H. Luke Ong
2014 J jnl
CoRR
Conrad Cotton-Barratt, Andrzej S. Murawski, C.-H. Luke Ong
2013 J jnl
CoRR
Emanuele D'Osualdo, Jonathan Kochems, C.-H. Luke Ong
2013 B conf
SAS
Emanuele D'Osualdo, Jonathan Kochems, C.-H. Luke Ong
2013 J jnl
CoRR
Martin Lester, C.-H. Luke Ong, Max Schäfer
2013 B conf
CONCUR
Jonathan Kochems, C.-H. Luke Ong
2012 A conf
ICFP
Robin P. Neatherway, Steven J. Ramsay, C.-H. Luke Ong
2012 A* conf
CAV
David Hopkins, Andrzej S. Murawski, C.-H. Luke Ong
2012 C ed.
WoLLIC
C.-H. Luke Ong, Ruy J. G. B. de Queiroz
2012 conf
ICALP (2)
C.-H. Luke Ong, Takeshi Tsukada
2011 Misc ed.
TASE
Zhenhua Duan, C.-H. Luke Ong
2011 conf
ICALP (2)
David Hopkins, Andrzej S. Murawski, C.-H. Luke Ong
2011 J jnl
Inf. Comput.
Matthew Hague, C.-H. Luke Ong
2011 J jnl
Log. Methods Comput. Sci.
Naoki Kobayashi, C.-H. Luke Ong
2011 conf
RTA
Jonathan Kochems, C.-H. Luke Ong
2011 ed.
TLCA
C.-H. Luke Ong
2011 A* conf
POPL
C.-H. Luke Ong, Steven J. Ramsay
2010 J jnl
CoRR
Matthew Hague, C.-H. Luke Ong
2010 conf
SPIN
Matthew Hague, C.-H. Luke Ong
2010 A conf
TACAS
Gérard Basler, Matthew Hague, Daniel Kroening, C.-H. Luke Ong, Thomas Wahl, Haoxian Zhao
2010 B ed.
FoSSaCS
C.-H. Luke Ong
2010 ch.
Logics and Languages for Reliability and Security
C.-H. Luke Ong
2010 A* conf
LICS
Christopher H. Broadbent, Arnaud Carayol, C.-H. Luke Ong, Olivier Serre
2009 A* conf
LICS
Naoki Kobayashi, C.-H. Luke Ong
2009 conf
ICALP (2)
Naoki Kobayashi, C.-H. Luke Ong
2009 A* conf
LICS
C.-H. Luke Ong, Nikos Tzevelekos
2009 A* conf
CAV
David Hopkins, C.-H. Luke Ong
2009 B conf
FoSSaCS
Christopher H. Broadbent, C.-H. Luke Ong
2009 J jnl
Log. Methods Comput. Sci.
William Blum, C.-H. Luke Ong
2009 B conf
CONCUR
Matthew Hague, C.-H. Luke Ong
2008 A* conf
LICS
Matthew Hague, Andrzej S. Murawski, C.-H. Luke Ong, Olivier Serre
2008 ed.
IFIP TCS
Giorgio Ausiello, Juhani Karhumäki, Giancarlo Mauri, C.-H. Luke Ong
2008 J jnl
CoRR
Matthew Hague, C.-H. Luke Ong
2008 J jnl
Log. Methods Comput. Sci.
Matthew Hague, C.-H. Luke Ong
2008 A conf
ESOP
C.-H. Luke Ong
2008 A* conf
LICS
Arnaud Carayol, Matthew Hague, Antoine Meyer, C.-H. Luke Ong, Olivier Serre
2007 conf
AOSD
Sam Bakhtiar Sanjabi, C.-H. Luke Ong
2007 B conf
MFCS
C.-H. Luke Ong
2007 B conf
FoSSaCS
Matthew Hague, C.-H. Luke Ong
2007 conf
TLCA
William Blum, C.-H. Luke Ong
2006 J jnl
ACM Trans. Comput. Log.
Andrzej S. Murawski, C.-H. Luke Ong
2006 A* conf
LICS
C.-H. Luke Ong
2006 B conf
CSL
C.-H. Luke Ong
2006 J jnl
Theor. Comput. Sci.
Dan R. Ghica, Andrzej S. Murawski, C.-H. Luke Ong
2005 B ed.
CSL
C.-H. Luke Ong
2005 A* conf
ICALP
Andrzej S. Murawski, C.-H. Luke Ong, Igor Walukiewicz
2005 B conf
FoSSaCS
Klaus Aehlig, Jolie G. de Miranda, C.-H. Luke Ong
2005 conf
TLCA
Klaus Aehlig, Jolie G. de Miranda, C.-H. Luke Ong
2004 J jnl
Ann. Pure Appl. Log.
C.-H. Luke Ong
2004 A conf
TACAS
Samson Abramsky, Dan R. Ghica, Andrzej S. Murawski, C.-H. Luke Ong
2004 J jnl
Theor. Comput. Sci.
C.-H. Luke Ong, Pietro Di Gianantonio
2004 A* conf
LICS
Samson Abramsky, Dan R. Ghica, Andrzej S. Murawski, C.-H. Luke Ong, Ian David Bede Stark
2004 J jnl
Theor. Comput. Sci.
Andrzej S. Murawski, C.-H. Luke Ong
2004 A* conf
ICALP
Dan R. Ghica, Andrzej S. Murawski, C.-H. Luke Ong
2003 J jnl
Theor. Comput. Sci.
Andrew D. Ker, Hanno Nickau, C.-H. Luke Ong
2003 J jnl
Theor. Comput. Sci.
Andrzej S. Murawski, C.-H. Luke Ong
2002 A* conf
ICALP
C.-H. Luke Ong, Pietro Di Gianantonio
2002 J jnl
Theor. Comput. Sci.
Andrew D. Ker, Hanno Nickau, C.-H. Luke Ong
2002 conf
FSTTCS
C.-H. Luke Ong
2002 A* conf
LICS
C.-H. Luke Ong
2001 conf
TLCA
Andrzej S. Murawski, C.-H. Luke Ong
2000 B conf
CSL
Andrzej S. Murawski, C.-H. Luke Ong
2000 A* conf
LICS
Andrzej S. Murawski, C.-H. Luke Ong
2000 B conf
APLAS
C.-H. Luke Ong
2000 J jnl
Inf. Comput.
J. M. E. Hyland, C.-H. Luke Ong
1999 B conf
CSL
Andrew D. Ker, Hanno Nickau, C.-H. Luke Ong
1999 conf
CTCS
Andrzej S. Murawski, C.-H. Luke Ong
1999 conf
CTCS
Thong wei Koh, C.-H. Luke Ong
1997 A* conf
POPL
C.-H. Luke Ong, Charles A. Stewart
1996 A* conf
LICS
C.-H. Luke Ong
1995 conf
FPCA
J. M. E. Hyland, C.-H. Luke Ong
1993 B conf
CSL
C.-H. Luke Ong, Eike Ritter
1993 J jnl
Inf. Comput.
Samson Abramsky, C.-H. Luke Ong
1993 conf
TLCA
J. M. E. Hyland, C.-H. Luke Ong
1993 A* conf
LICS
C.-H. Luke Ong
1992 A* conf
ICALP
C.-H. Luke Ong
1988 A* conf
FOCS
C.-H. Luke Ong
tests/unit/test_apk_dataclasses.py
← Index tests/unit/test_apk_dataclasses.py python
"""
Unit tests for APK dataclasses in redb/models/dataclasses.py.
"""
import pytest
from dataclasses import asdict, fields

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


# ============================================================================
# APKFeatures
# ============================================================================

class TestAPKFeaturesDataclass:
    """Tests for APKFeatures dataclass."""

    def test_named_fields(self):
        from redb.models.dataclasses import APKFeatures

        f = APKFeatures(
            package_name="com.example.app",
            app_name="Example App",
            version_code=1,
            version_name="1.0.0",
        )
        assert f.package_name == "com.example.app"
        assert f.app_name == "Example App"
        assert f.version_code == 1
        assert f.version_name == "1.0.0"

    def test_all_fields_default_none_or_safe(self):
        from redb.models.dataclasses import APKFeatures

        f = APKFeatures()
        # Nullable fields default to None
        assert f.package_name is None
        assert f.app_name is None
        assert f.version_code is None
        assert f.version_name is None
        assert f.min_sdk_version is None
        assert f.target_sdk_version is None
        assert f.compile_sdk_version is None
        assert f.main_activity is None
        # Non-nullable fields have safe defaults
        assert f.is_debuggable is False
        assert f.allow_backup is True
        assert f.uses_cleartext_traffic is False
        assert f.supported_abis == []
        assert f.dex_count == 0
        assert f.total_dex_size == 0
        assert f.total_file_count == 0
        assert f.has_native_code is False
        assert f.has_assets is False
        assert f.uses_libraries == []
        assert f.earliest_content_modification is None
        assert f.latest_content_modification is None
        assert f.contains_embedded_apk is False

    def test_all_fields_populated(self):
        from redb.models.dataclasses import APKFeatures

        f = APKFeatures(
            package_name="com.malware.test",
            app_name="Malware",
            version_code=42,
            version_name="6.6.6",
            min_sdk_version=21,
            target_sdk_version=33,
            compile_sdk_version=33,
            main_activity="com.malware.test.MainActivity",
            is_debuggable=True,
            allow_backup=False,
            uses_cleartext_traffic=True,
            supported_abis=["armeabi-v7a", "arm64-v8a", "x86"],
            dex_count=3,
            total_dex_size=5000000,
            total_file_count=200,
            has_native_code=True,
            has_assets=True,
            uses_libraries=["com.google.android.maps"],
            earliest_content_modification="2024-01-01T00:00:00Z",
            latest_content_modification="2024-06-15T12:00:00Z",
            contains_embedded_apk=True,
        )
        assert f.is_debuggable is True
        assert f.dex_count == 3
        assert len(f.supported_abis) == 3
        assert f.contains_embedded_apk is True

    def test_asdict_roundtrip(self):
        from redb.models.dataclasses import APKFeatures

        f = APKFeatures(
            package_name="com.test",
            app_name="Test",
            version_code=1,
            version_name="1.0",
        )
        d = asdict(f)
        assert d["package_name"] == "com.test"
        assert d["is_debuggable"] is False
        assert d["supported_abis"] == []

    def test_partial_extraction(self):
        """Verify that APKFeatures can be created with only some fields populated,
        simulating a partial extraction where some fields failed."""
        from redb.models.dataclasses import APKFeatures

        f = APKFeatures(
            package_name="com.example.app",
            # app_name failed to extract — left as None
            version_code=10,
            # version_name failed to extract — left as None
            is_debuggable=True,
            dex_count=2,
        )
        assert f.package_name == "com.example.app"
        assert f.app_name is None
        assert f.version_code == 10
        assert f.version_name is None
        assert f.is_debuggable is True
        assert f.dex_count == 2


# ============================================================================
# APKManifestComponent
# ============================================================================

class TestAPKManifestComponentDataclass:

    def test_creation(self):
        from redb.models.dataclasses import APKManifestComponent

        c = APKManifestComponent(
            component_type="activity",
            class_name="com.example.MainActivity",
            is_exported=True,
            intent_actions=["android.intent.action.MAIN"],
            intent_categories=["android.intent.category.LAUNCHER"],
        )
        assert c.component_type == "activity"
        assert c.class_name == "com.example.MainActivity"
        assert c.is_exported is True
        assert len(c.intent_actions) == 1
        assert len(c.intent_categories) == 1

    def test_defaults(self):
        from redb.models.dataclasses import APKManifestComponent

        c = APKManifestComponent(
            component_type="service",
            class_name="com.example.MyService",
            is_exported=False,
        )
        assert c.intent_actions == []
        assert c.intent_categories == []


# ============================================================================
# APKManifest
# ============================================================================

class TestAPKManifestDataclass:

    def test_required_fields(self):
        from redb.models.dataclasses import APKManifest

        m = APKManifest(
            activity_count=5,
            service_count=2,
            receiver_count=3,
            provider_count=1,
        )
        assert m.activity_count == 5
        assert m.service_count == 2
        assert m.receiver_count == 3
        assert m.provider_count == 1

    def test_optional_list_defaults(self):
        from redb.models.dataclasses import APKManifest

        m = APKManifest(
            activity_count=0,
            service_count=0,
            receiver_count=0,
            provider_count=0,
        )
        assert m.activities == []
        assert m.services == []
        assert m.receivers == []
        assert m.providers == []
        assert m.exported_components == []
        assert m.intent_filters_by_action == []
        assert m.intent_filters_by_category == []
        assert m.uses_features == []
        assert m.meta_data is None
        assert m.manifest_xml is None

    def test_fully_populated(self):
        from redb.models.dataclasses import APKManifest

        m = APKManifest(
            activity_count=2,
            service_count=1,
            receiver_count=1,
            provider_count=0,
            activities=["MainActivity", "SettingsActivity"],
            services=["SyncService"],
            receivers=["BootReceiver"],
            providers=[],
            exported_components=["MainActivity", "BootReceiver"],
            intent_filters_by_action=["android.intent.action.MAIN"],
            uses_features=["android.hardware.camera"],
            manifest_xml="<manifest>...</manifest>",
        )
        assert len(m.activities) == 2
        assert len(m.exported_components) == 2
        assert m.manifest_xml is not None


# ============================================================================
# APKPermission / APKPermissions
# ============================================================================

class TestAPKPermissionDataclass:

    def test_creation(self):
        from redb.models.dataclasses import APKPermission

        p = APKPermission(
            permission_name="android.permission.CAMERA",
            protection_level="dangerous",
        )
        assert p.permission_name == "android.permission.CAMERA"
        assert p.protection_level == "dangerous"
        assert p.is_custom is False

    def test_custom_permission(self):
        from redb.models.dataclasses import APKPermission

        p = APKPermission(
            permission_name="com.example.CUSTOM_PERM",
            protection_level="signature",
            is_custom=True,
        )
        assert p.is_custom is True


class TestAPKPermissionsDataclass:

    def test_required_fields(self):
        from redb.models.dataclasses import APKPermissions

        p = APKPermissions(
            total_permission_count=10,
            dangerous_permission_count=3,
        )
        assert p.total_permission_count == 10
        assert p.dangerous_permission_count == 3

    def test_defaults(self):
        from redb.models.dataclasses import APKPermissions

        p = APKPermissions(
            total_permission_count=0,
            dangerous_permission_count=0,
        )
        assert p.permissions == []
        assert p.dangerous_permissions == []
        assert p.custom_permissions == []
        assert p.permission_details == []
        assert p.permhash is None

    def test_with_permission_details(self):
        from redb.models.dataclasses import APKPermission, APKPermissions

        details = [
            APKPermission("android.permission.INTERNET", "normal"),
            APKPermission("android.permission.CAMERA", "dangerous"),
        ]
        p = APKPermissions(
            total_permission_count=2,
            dangerous_permission_count=1,
            permissions=["android.permission.INTERNET", "android.permission.CAMERA"],
            dangerous_permissions=["android.permission.CAMERA"],
            permission_details=details,
            permhash="abc123def456",
        )
        assert len(p.permission_details) == 2
        assert p.permhash == "abc123def456"


# ============================================================================
# APKCertificate / APKCodeSigningInfo
# ============================================================================

class TestAPKCertificateDataclass:

    def test_required_fields(self):
        from redb.models.dataclasses import APKCertificate

        c = APKCertificate(
            subject="CN=Android Debug",
            issuer="CN=Android Debug",
            serial_number="1234567890",
            valid_from="2024-01-01",
            valid_to="2054-01-01",
            thumbprint_sha1="aabbccdd" * 5,
            thumbprint_sha256="aabbccdd" * 8,
        )
        assert c.subject == "CN=Android Debug"
        assert c.issuer == "CN=Android Debug"
        assert c.is_self_signed is False

    def test_optional_fields(self):
        from redb.models.dataclasses import APKCertificate

        c = APKCertificate(
            subject="CN=Test",
            issuer="CN=CA",
            serial_number="1",
            valid_from="2024-01-01",
            valid_to="2025-01-01",
            thumbprint_sha1="aa" * 20,
            thumbprint_sha256="bb" * 32,
            algorithm="SHA256withRSA",
            key_size=2048,
            is_self_signed=True,
        )
        assert c.algorithm == "SHA256withRSA"
        assert c.key_size == 2048
        assert c.is_self_signed is True


class TestAPKCodeSigningInfoDataclass:

    def test_required_fields(self):
        from redb.models.dataclasses import APKCodeSigningInfo

        info = APKCodeSigningInfo(
            _id="abcdef1234567890" * 4,
            is_signed=True,
        )
        assert info.is_signed is True
        assert info.signature_scheme_versions == []
        assert info.number_of_certificates == 0
        assert info.x509_certificates is None
        assert info.apk_signer_subject is None
        assert info.apk_signer_issuer is None

    def test_fully_populated(self):
        from redb.models.dataclasses import APKCertificate, APKCodeSigningInfo

        cert = APKCertificate(
            subject="CN=Test",
            issuer="CN=Test",
            serial_number="1",
            valid_from="2024-01-01",
            valid_to="2025-01-01",
            thumbprint_sha1="aa" * 20,
            thumbprint_sha256="bb" * 32,
        )
        info = APKCodeSigningInfo(
            _id="cc" * 32,
            is_signed=True,
            signature_scheme_versions=[1, 2, 3],
            number_of_certificates=1,
            x509_certificates=[cert],
            apk_signer_subject="CN=Test",
            apk_signer_issuer="CN=Test",
        )
        assert len(info.signature_scheme_versions) == 3
        assert info.number_of_certificates == 1
        assert len(info.x509_certificates) == 1


# ============================================================================
# APKDexFile
# ============================================================================

class TestAPKDexFileDataclass:

    def test_required_fields(self):
        from redb.models.dataclasses import APKDexFile

        d = APKDexFile(
            filename="classes.dex",
            sha256="aa" * 32,
            class_count=100,
            method_count=500,
            string_count=1000,
        )
        assert d.filename == "classes.dex"
        assert d.class_count == 100
        assert d.method_count == 500
        assert d.string_count == 1000

    def test_optional_fields_defaults(self):
        from redb.models.dataclasses import APKDexFile

        d = APKDexFile(
            filename="classes2.dex",
            sha256="bb" * 32,
            class_count=0,
            method_count=0,
            string_count=0,
        )
        assert d.tlsh is None
        assert d.top_packages is None
        assert d.api_usage is None
        assert d.obfuscation_indicators is None

    def test_tlsh_populated(self):
        from redb.models.dataclasses import APKDexFile

        tlsh_value = "T1" + "A" * 68
        d = APKDexFile(
            filename="classes.dex",
            sha256="aa" * 32,
            class_count=100,
            method_count=500,
            string_count=1000,
            tlsh=tlsh_value,
        )
        assert d.tlsh == tlsh_value

    def test_with_api_usage(self):
        from redb.models.dataclasses import APKDexFile

        d = APKDexFile(
            filename="classes.dex",
            sha256="cc" * 32,
            class_count=50,
            method_count=200,
            string_count=300,
            top_packages=[{"name": "com.example", "class_count": 30}],
            api_usage={
                "crypto": ["Ljavax/crypto/Cipher;->getInstance"],
                "reflection": ["Ljava/lang/Class;->forName"],
            },
            obfuscation_indicators={"short_class_names_ratio": 0.8, "single_char_methods_ratio": 0.5},
        )
        assert len(d.api_usage) == 2
        assert d.obfuscation_indicators["short_class_names_ratio"] == 0.8


# ============================================================================
# APKResource
# ============================================================================

class TestAPKResourceDataclass:

    def test_creation(self):
        from redb.models.dataclasses import APKResource

        r = APKResource(
            path="res/drawable/icon.png",
            size=1024,
            sha256="dd" * 32,
            filetype_magika="png",
        )
        assert r.path == "res/drawable/icon.png"
        assert r.size == 1024
        assert r.is_suspicious is False

    def test_suspicious_resource(self):
        from redb.models.dataclasses import APKResource

        r = APKResource(
            path="assets/payload.dex",
            size=50000,
            sha256="ee" * 32,
            filetype_magika="dex",
            is_suspicious=True,
        )
        assert r.is_suspicious is True


# ============================================================================
# APKNativeLib
# ============================================================================

class TestAPKNativeLibDataclass:

    def test_creation(self):
        from redb.models.dataclasses import APKNativeLib

        lib = APKNativeLib(
            abi="arm64-v8a",
            filename="libnative.so",
            size=200000,
            sha256="ff" * 32,
        )
        assert lib.abi == "arm64-v8a"
        assert lib.filename == "libnative.so"
        assert lib.is_known_packer is False

    def test_known_packer(self):
        from redb.models.dataclasses import APKNativeLib

        lib = APKNativeLib(
            abi="armeabi-v7a",
            filename="libjiagu.so",
            size=100000,
            sha256="aa" * 32,
            is_known_packer=True,
        )
        assert lib.is_known_packer is True


# ============================================================================
# APKInconsistencyTests
# ============================================================================

class TestAPKInconsistencyTestsDataclass:

    def test_all_defaults_none(self):
        from redb.models.dataclasses import APKInconsistencyTests

        t = APKInconsistencyTests()
        assert t.test_zip_bomb is None
        assert t.test_zip_duplicate_entries is None
        assert t.test_zip_path_traversal is None
        assert t.test_zip_suspicious_timestamps is None
        assert t.test_hidden_dex_files is None
        assert t.test_manifest_component_mismatch is None
        assert t.test_debuggable_release is None
        assert t.test_emulator_detection_strings is None
        assert t.test_debugger_detection is None
        assert t.test_root_detection is None

    def test_all_fields_populated(self):
        from redb.models.dataclasses import APKInconsistencyTests

        t = APKInconsistencyTests(
            test_zip_bomb=True,
            test_zip_duplicate_entries=False,
            test_zip_path_traversal=True,
            test_zip_suspicious_timestamps=False,
            test_hidden_dex_files=True,
            test_manifest_component_mismatch=False,
            test_debuggable_release=True,
            test_emulator_detection_strings=True,
            test_debugger_detection=False,
            test_root_detection=True,
        )
        assert t.test_zip_bomb is True
        assert t.test_debuggable_release is True
        # Count true values
        d = asdict(t)
        true_count = sum(1 for v in d.values() if v is True)
        assert true_count == 6

    def test_field_count(self):
        from redb.models.dataclasses import APKInconsistencyTests

        # Exactly 10 inconsistency tests per PDD specification
        assert len(fields(APKInconsistencyTests)) == 10


# ============================================================================
# Hashes dataclass — APK permhash field
# ============================================================================

class TestHashesAPKField:

    def test_permhash_default_none(self):
        from redb.models.dataclasses import Hashes

        h = Hashes(md5="a", sha1="b", sha256="c", ssdeep_hash="d", tlsh_hash="e")
        assert h.permhash is None

    def test_permhash_populated(self):
        from redb.models.dataclasses import Hashes

        h = Hashes(
            md5="a", sha1="b", sha256="c", ssdeep_hash="d", tlsh_hash="e",
            permhash="abc123" * 10 + "abcd",
        )
        assert h.permhash is not None
        assert len(h.permhash) == 64


# ============================================================================
# Tag enum — APK entries
# ============================================================================

class TestAPKTagEnum:

    def test_all_apk_tags_exist(self):
        from redb.extractors.enum import Tag

        expected_tags = [
            "APK_FEATURES", "APK_MANIFEST", "APK_PERMISSIONS", "APK_SIGNATURE",
            "APK_DEX", "APK_RESOURCES", "APK_NATIVE_LIBS", "APK_INCONSISTENCY_TESTS",
        ]
        for name in expected_tags:
            assert hasattr(Tag, name), f"Tag.{name} missing"

    def test_apk_tag_values(self):
        from redb.extractors.enum import Tag

        assert Tag.APK_FEATURES.value == "apk_features"
        assert Tag.APK_MANIFEST.value == "apk_manifest"
        assert Tag.APK_PERMISSIONS.value == "apk_permissions"
        assert Tag.APK_SIGNATURE.value == "apk_signature"
        assert Tag.APK_DEX.value == "apk_dex"
        assert Tag.APK_RESOURCES.value == "apk_resources"
        assert Tag.APK_NATIVE_LIBS.value == "apk_native_libs"
        assert Tag.APK_INCONSISTENCY_TESTS.value == "apk_inconsistency_tests"