Hanane Azzag

95 papers A* 3B 16C 5Journal 28Unranked 40
YearRankTypeTitle / Venue / Authors
2026 J jnl
IEEE Trans. Cybern.
Djamel Bouchaffra, Fayçal Ykhlef, Bilal Faye, Mustapha Lebbah, Hanane Azzag
2026 J jnl
J. Intell. Inf. Syst.
Annalisa Appice, Hanane Azzag, Mohand-Said Hacid, Allel Hadjali
2025 J jnl
Data Knowl. Eng.
Bilal Faye, Hanane Azzag, Mustapha Lebbah, Fangchen Feng
2025 conf
EGC
Abdellah Madane, Florent Forest, Hanane Azzag, Mustapha Lebbah, Jérôme Lacaille
2025 A* conf
EMNLP
Quang Anh Nguyen, Nadi Tomeh, Mustapha Lebbah, Thierry Charnois, Hanane Azzag
2025 J jnl
CoRR
Karim Radouane, Hanane Azzag, Mustapha Lebbah
2025 J jnl
Neural Comput. Appl.
Bilal Faye, Hanane Azzag, Mustapha Lebbah, Djamel Bouchaffra
2025 J jnl
CoRR
Bilal Faye, Hanane Azzag, Mustapha Lebbah
2025 conf
EGC
Abdellah Madane, Florent Forest, Hanane Azzag, Mustapha Lebbah, Jérôme Lacaille
2025 J jnl
CoRR
Bilal Faye, Hanane Azzag, Mustapha Lebbah
2024 A* conf
IJCAI
Abdellah Madane, Florent Forest, Hanane Azzag, Mustapha Lebbah, Jérôme Lacaille
2024 B conf
ICIP
Bilal Faye, Hanane Azzag, Mustapha Lebbah, Djamel Bouchaffra
2024 J jnl
CoRR
Bilal Faye, Hanane Azzag, Mustapha Lebbah, Djamel Bouchaffra
2024 J jnl
CoRR
Bilal Faye, Hanane Azzag, Mustapha Lebbah
2024 J jnl
CoRR
Bilal Faye, Hanane Azzag, Mustapha Lebbah, Djamel Bouchaffra
2024 J jnl
Neural Comput. Appl.
Kodjo Mawuena Amekoe, Hanane Azzag, Zaineb Chelly Dagdia, Mustapha Lebbah, Gregoire Jaffre
2024 C ed.
ISMIS
Annalisa Appice, Hanane Azzag, Mohand-Said Hacid, Allel Hadjali, Zbigniew W. Ras
2024 B conf
ESANN
Abdellah Madane, Florent Forest, Hanane Azzag, Mustapha Lebbah, Jérôme Lacaille
2024 J jnl
CoRR
Djamel Bouchaffra, Fayçal Ykhlef, Bilal Faye, Hanane Azzag, Mustapha Lebbah
2024 J jnl
CoRR
Binta Sow, Bilal Faye, Hanane Azzag, Mustapha Lebbah
2024 B conf
ESANN
Bilal Faye, Hanane Azzag, Mustapha Lebbah, Djamel Bouchaffra
2024 conf
EGC
Bilal Faye, Hanane Azzag, Mustapha Lebbah, Fangchen Fang
2024 B conf
IJCNN
Abdellah Madane, Florent Forest, Hanane Azzag, Mustapha Lebbah, Jérôme Lacaille
2024 J jnl
CoRR
Bilal Faye, Hanane Azzag, Mustapha Lebbah
2024 J jnl
CoRR
Bilal Faye, Mustapha Lebbah, Hanane Azzag
2024 B conf
IJCNN
Bilal Faye, Hanane Azzag, Mustapha Lebbah, Fangchen Fang
2024 J jnl
CoRR
Bilal Faye, Hanane Azzag, Mustapha Lebbah, Fangchen Fang
2023 conf
PKDD/ECML Workshops (3)
Kodjo Mawuena Amekoe, Hanane Azzag, Mustapha Lebbah, Zaineb Chelly Dagdia, Gregoire Jaffre
2023 conf
ICDM (Workshops)
Bilal Faye, Hanane Azzag, Mustapha Lebbah, Mohamed Djallel Dilmi, Djamel Bouchaffra
2023 J jnl
CoRR
Bilal Faye, Mohamed Djallel Dilmi, Hanane Azzag, Mustapha Lebbah, Fangchen Feng
2023 conf
PAKDD (1)
Alex Mourer, Florent Forest, Mustapha Lebbah, Hanane Azzag, Jérôme Lacaille
2022 J jnl
Comput. Stat. Data Anal.
Étienne Goffinet, Mustapha Lebbah, Hanane Azzag, Loïc Giraldi, Anthony Coutant
2022 J jnl
CoRR
Abdellah Madane, Mohamed Djallel Dilmi, Florent Forest, Hanane Azzag, Mustapha Lebbah, Jérôme Lacaille
2021 B conf
ESANN
Étienne Goffinet, Mustapha Lebbah, Hanane Azzag, Loïc Giraldi, Anthony Coutant
2021 conf
AALTD@ECML/PKDD
Étienne Goffinet, Mustapha Lebbah, Hanane Azzag, Loïc Giraldi, Anthony Coutant
2020 J jnl
CoRR
Florent Forest, Mustapha Lebbah, Hanane Azzag, Jérôme Lacaille
2020 B conf
ICPR
Florent Forest, Alex Mourer, Mustapha Lebbah, Hanane Azzag, Jérôme Lacaille
2020 conf
ECML/PKDD (4)
Étienne Goffinet, Mustapha Lebbah, Hanane Azzag, Loïc Giraldi
2020 conf
EGC
Étienne Goffinet, Mustapha Lebbah, Hanane Azzag, Loïc Giraldi
2020 J jnl
CoRR
Étienne Goffinet, Anthony Coutant, Mustapha Lebbah, Hanane Azzag, Loïc Giraldi
2020 J jnl
CoRR
Alex Mourer, Florent Forest, Mustapha Lebbah, Hanane Azzag, Jérôme Lacaille
2019 ed.
IV (1)
Ebad Banissi, Anna Ursyn, Mark W. McK. Bannatyne, Nuno Datia, Rita Francese, Muhammad Sarfraz, Theodor G. Wyeld, Fatma Bouali, Gilles Venturini, Hanane Azzag, Mustapha Lebbah, Marjan Trutschl, Urska Cvek, Heimo Müller, Minoru Nakayama, Sebastian Kernbach, Loredana Caruccio, Michele Risi, Ugo Erra, Autilia Vitiello, Veronica Rossano
2019 J jnl
CoRR
Gaël Beck, Tarn Duong, Mustapha Lebbah, Hanane Azzag, Christophe Cérin
2019 J jnl
J. Parallel Distributed Comput.
Gaël Beck, Tarn Duong, Mustapha Lebbah, Hanane Azzag, Christophe Cérin
2019 conf
PAKDD (Workshops)
Andriantsiory Dina Faneva, Mustapha Lebbah, Hanane Azzag, Gaël Beck
2019 J jnl
CoRR
Andriantsiory Dina Faneva, Mustapha Lebbah, Hanane Azzag, Gaël Beck
2019 conf
PAKDD (Workshops)
Florent Forest, Mustapha Lebbah, Hanane Azzag, Jérôme Lacaille
2019 J jnl
CoRR
Gaël Beck, Tarn Duong, Mustapha Lebbah, Hanane Azzag
2018 conf
IEEE BigData
Mohammed Ghesmoune, Mustapha Lebbah, Hanane Azzag, Salima Benbernou, Mourad Ouziri, Tarn Duong
2018 conf
INNS Conference on Big Data
Gaël Beck, Hanane Azzag, Stéphanie Bougeard, Mustapha Lebbah, Ndèye Niang
2018 ed.
EGC
Mustapha Lebbah, Christine Largeron, Hanane Azzag
2018 B conf
IJCNN
Nhat-Quang Doan, Hanane Azzag, Mustapha Lebbah
2018 conf
EGC
Gaël Beck, Hanane Azzag, Mustapha Lebbah, Tarn Duong
2017 conf
EGC
Hippolyte Léger, Dominique Bouthinon, Mustapha Lebbah, Hanane Azzag
2016 B conf
KES
Nesrine Masmoudi, Hanane Azzag, Mustapha Lebbah, Cyrille Bertelle, Maher Ben Jemaa
2015 conf
ICONIP (1)
Nesrine Masmoudi, Hanane Azzag, Mustapha Lebbah, Cyrille Bertelle, Maher Ben Jemaa
2015 conf
EGC
Mohammed Ghesmoune, Mustapha Lebbah, Hanane Azzag
2015 B conf
IJCNN
Nhat-Quang Doan, Mohammed Ghesmoune, Hanane Azzag, Mustapha Lebbah
2015 B conf
CEC
Nesrine Masmoudi, Hanane Azzag, Mustapha Lebbah, Cyrille Bertelle, Maher Ben Jemaa
2015 conf
EGC
Tugdual Sarazin, Hanane Azzag, Mustapha Lebbah
2014 conf
IEEE BigData
Tugdual Sarazin, Mustapha Lebbah, Hanane Azzag
2014 ch.
Fouille de données complexes
Amine Chaibi, Mustapha Lebbah, Hanane Azzag
2014 conf
ICONIP (2)
Tugdual Sarazin, Mustapha Lebbah, Hanane Azzag, Amine Chaibi
2014 conf
NaBIC
Nesrine Masmoudi, Hanane Azzag, Mustapha Lebbah, Cyrille Bertelle
2014 conf
EGC
Amine Chaibi, Hanane Azzag, Mustapha Lebbah
2014 conf
IPDPS Workshops
Tugdual Sarazin, Hanane Azzag, Mustapha Lebbah
2013 C ed.
IV
Ebad Banissi, Hanane Azzag, Mark W. McK. Bannatyne, Stefan Bertschi, Fatma Bouali, Remo Burkhard, John Counsell, Alfredo Cuzzocrea, Martin J. Eppler, Barbara Hammer, Mustapha Lebbah, Francis T. Marchese, Muhammad Sarfraz, Anna Ursyn, Gilles Venturini, Theodor G. Wyeld
2013 C conf
IV
Amine Chaibi, Mustapha Lebbah, Hanane Azzag
2013 B conf
IJCNN
Amine Chaibi, Mustapha Lebbah, Hanane Azzag
2013 conf
NaBIC
Nesrine Masmoudi, Hanane Azzag, Mustapha Lebbah, Cyrille Bertelle
2013 J jnl
Int. J. Comput. Intell. Appl.
Amine Chaibi, Mustapha Lebbah, Hanane Azzag
2013 conf
EGC
Amine Chaibi, Mustapha Lebbah, Hanane Azzag
2013 B conf
IJCNN
Nhat-Quang Doan, Hanane Azzag, Mustapha Lebbah, Guillaume Santini
2013 conf
EGC
Nhat-Quang Doan, Hanane Azzag, Mustapha Lebbah
2012 C ed.
IV
Ebad Banissi, Stefan Bertschi, Camilla Forsell, Jimmy Johansson, Sarah Kenderdine, Francis T. Marchese, Muhammad Sarfraz, Liz J. Stuart, Anna Ursyn, Theodor G. Wyeld, Hanane Azzag, Mustapha Lebbah, Gilles Venturini
2012 J jnl
Adapt. Behav.
Hanane Azzag, Christiane Guinot, Gilles Venturini
2012 B conf
ESANN
Amine Chaibi, Hanane Azzag, Mustapha Lebbah
2012 conf
EGC
Nhat-Quang Doan, Hanane Azzag, Mustapha Lebbah
2012 conf
EGC
Amine Chaibi, Mustapha Lebbah, Hanane Azzag
2012 C conf
IV
Nhat-Quang Doan, Hanane Azzag, Mustapha Lebbah
2012 B conf
IJCNN
Nhat-Quang Doan, Hanane Azzag, Mustapha Lebbah
2012 conf
ICONIP (3)
Amine Chaibi, Mustapha Lebbah, Hanane Azzag
2012 conf
ICANN (2)
Nhat-Quang Doan, Hanane Azzag, Mustapha Lebbah
2011 conf
EGC (best of volume)
Hanane Azzag, Mustapha Lebbah
2011 conf
GECCO (Companion)
Lydia Boudjeloud, Hanane Azzag
2011 conf
EGC
Hanane Azzag, Mustapha Lebbah
2010 conf
EGC
Lydia Boudjeloud-Assala, Hanane Azzag
2010 conf
EGC
Hanane Azzag, Mustapha Lebbah
2010 conf
ICONIP (1)
Mustapha Lebbah, Hanane Azzag
2009 conf
ECAL (2)
Hanane Azzag, Mustapha Lebbah
2008 B conf
ESANN
Hanane Azzag, Mustapha Lebbah
2008 conf
EGC
Mustapha Lebbah, Hanane Azzag
2007 conf
ICTAI (1)
Julien Lavergne, Hanane Azzag, Christiane Guinot, Gilles Venturini
2007 A* conf
WWW
Hanane Azzag, Julien Lavergne, Christiane Guinot, Gilles Venturini
2006 conf
AAFD
Hanane Azzag, David Da Costa, Christiane Guinot, Gilles Venturini
tests/unit/test_smali_normalization.py
← Index tests/unit/test_smali_normalization.py python
"""Unit tests for smali semantic normalization.

Tests the three normalization levels (category, opcode, opcode_api),
opcode categorization, ACFG feature index mapping, and integration
with MinHash computation.
"""
import pytest

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


class TestCategorizeOpcode:
    """Tests for opcode -> semantic category mapping."""

    def test_arithmetic(self):
        from redb.extractors.decompiler.apk.smali_normalization import categorize_opcode
        assert categorize_opcode("add-int") == "ALU"
        assert categorize_opcode("mul-long/2addr") == "ALU"
        assert categorize_opcode("neg-int") == "ALU"

    def test_data_movement(self):
        from redb.extractors.decompiler.apk.smali_normalization import categorize_opcode
        assert categorize_opcode("move-result") == "MOV"
        assert categorize_opcode("const/4") == "CONST"
        assert categorize_opcode("const-string") == "CONST"

    def test_memory_access(self):
        from redb.extractors.decompiler.apk.smali_normalization import categorize_opcode
        assert categorize_opcode("iget-object") == "LOAD"
        assert categorize_opcode("sput-wide") == "STORE"
        assert categorize_opcode("aget-byte") == "LOAD"

    def test_invocation(self):
        from redb.extractors.decompiler.apk.smali_normalization import categorize_opcode
        assert categorize_opcode("invoke-virtual") == "CALL"
        assert categorize_opcode("invoke-static") == "CALL"
        assert categorize_opcode("invoke-direct") == "CALL"

    def test_control_flow(self):
        from redb.extractors.decompiler.apk.smali_normalization import categorize_opcode
        assert categorize_opcode("if-eqz") == "BRANCH"
        assert categorize_opcode("goto") == "JMP"
        assert categorize_opcode("goto/16") == "JMP"
        assert categorize_opcode("return-void") == "RET"

    def test_comparison(self):
        from redb.extractors.decompiler.apk.smali_normalization import categorize_opcode
        assert categorize_opcode("cmp-long") == "CMP"
        assert categorize_opcode("cmpl-float") == "CMP"
        assert categorize_opcode("cmpg-double") == "CMP"

    def test_conversion(self):
        from redb.extractors.decompiler.apk.smali_normalization import categorize_opcode
        assert categorize_opcode("int-to-long") == "CONV"
        assert categorize_opcode("float-to-int") == "CONV"

    def test_object_type(self):
        from redb.extractors.decompiler.apk.smali_normalization import categorize_opcode
        assert categorize_opcode("new-instance") == "ALLOC"
        assert categorize_opcode("check-cast") == "TYPE"
        assert categorize_opcode("instance-of") == "TYPE"

    def test_exception_sync(self):
        from redb.extractors.decompiler.apk.smali_normalization import categorize_opcode
        assert categorize_opcode("throw") == "EXC"
        assert categorize_opcode("monitor-enter") == "SYNC"

    def test_unknown(self):
        from redb.extractors.decompiler.apk.smali_normalization import categorize_opcode
        assert categorize_opcode("nop") == "OTHER"


class TestNormalizeInstruction:
    """Tests for single instruction normalization at various levels."""

    def test_category_level(self):
        from redb.extractors.decompiler.apk.smali_normalization import normalize_instruction
        assert normalize_instruction("add-int v0, v1, v2", "category") == "ALU"
        assert normalize_instruction("invoke-virtual {p0}, Lcom/Foo;->bar()V", "category") == "CALL"
        assert normalize_instruction("return-void", "category") == "RET"

    def test_opcode_level_strips_width(self):
        from redb.extractors.decompiler.apk.smali_normalization import normalize_instruction
        # Width variants collapse to same base
        assert normalize_instruction("add-int v0, v1, v2", "opcode") == "add"
        assert normalize_instruction("add-long v0, v2, v4", "opcode") == "add"
        assert normalize_instruction("add-float v0, v1, v2", "opcode") == "add"
        # Addressing modes collapse too
        assert normalize_instruction("add-int/2addr v0, v1", "opcode") == "add"

    def test_opcode_api_invoke(self):
        from redb.extractors.decompiler.apk.smali_normalization import normalize_instruction
        result = normalize_instruction(
            "invoke-virtual {p0, v0}, Lcom/example/Foo;->bar(I)V"
        )
        assert result == "CALL Lcom/example/Foo;->bar(I)V"

    def test_opcode_api_invoke_no_ref(self):
        from redb.extractors.decompiler.apk.smali_normalization import normalize_instruction
        # If somehow no method ref is found, fallback to CALL
        assert normalize_instruction("invoke-virtual {v0}") == "CALL"

    def test_opcode_api_field_load(self):
        from redb.extractors.decompiler.apk.smali_normalization import normalize_instruction
        result = normalize_instruction(
            "iget-object v0, p0, Lcom/Foo;->mField:Ljava/lang/String;"
        )
        assert result == "LOAD Lcom/Foo;->mField:Ljava/lang/String;"

    def test_opcode_api_field_store(self):
        from redb.extractors.decompiler.apk.smali_normalization import normalize_instruction
        result = normalize_instruction(
            "iput-object v0, p0, Lcom/Foo;->mField:Ljava/lang/String;"
        )
        assert result == "STORE Lcom/Foo;->mField:Ljava/lang/String;"

    def test_opcode_api_androguard_field_format(self):
        """androguard uses space instead of colon in field references."""
        from redb.extractors.decompiler.apk.smali_normalization import normalize_instruction
        result = normalize_instruction(
            "iget-object v0, p0, Lcom/Foo;->mField Ljava/lang/String;"
        )
        assert result.startswith("LOAD")
        assert "Lcom/Foo;->mField" in result

    def test_opcode_api_new_instance(self):
        from redb.extractors.decompiler.apk.smali_normalization import normalize_instruction
        result = normalize_instruction(
            "new-instance v0, Lcom/example/MyClass;"
        )
        assert result == "ALLOC Lcom/example/MyClass;"

    def test_opcode_api_const_string(self):
        from redb.extractors.decompiler.apk.smali_normalization import normalize_instruction
        result = normalize_instruction(
            'const-string v0, "hello world"'
        )
        assert result == 'CONST_STR "hello world"'

    def test_opcode_api_const_string_jumbo(self):
        from redb.extractors.decompiler.apk.smali_normalization import normalize_instruction
        result = normalize_instruction(
            'const-string/jumbo v0, "large string"'
        )
        assert result == 'CONST_STR "large string"'

    def test_opcode_api_fallback_to_category(self):
        from redb.extractors.decompiler.apk.smali_normalization import normalize_instruction
        # Non-special instructions fall back to category
        assert normalize_instruction("add-int v0, v1, v2") == "ALU"
        assert normalize_instruction("return-void") == "RET"
        assert normalize_instruction("nop") == "OTHER"

    def test_empty_input(self):
        from redb.extractors.decompiler.apk.smali_normalization import normalize_instruction
        assert normalize_instruction("") == ""
        assert normalize_instruction("   ") == ""


class TestNormalizeMethodBody:
    """Tests for full method body normalization."""

    def test_filters_directives_labels_comments(self):
        from redb.extractors.decompiler.apk.smali_normalization import normalize_method_body
        body = """\
.locals 2
# comment
:label_0
add-int v0, v1, v2
.line 42
invoke-virtual {p0}, Lcom/Foo;->bar()V
return-void
"""
        result = normalize_method_body(body, "category")
        assert result == ["ALU", "CALL", "RET"]

    def test_opcode_api_preserves_api_refs(self):
        from redb.extractors.decompiler.apk.smali_normalization import normalize_method_body
        body = """\
const/4 v0, 0x0
invoke-virtual {p0, v0}, Lcom/Foo;->setX(I)V
iget-object v1, p0, Lcom/Foo;->name:Ljava/lang/String;
return-void
"""
        result = normalize_method_body(body, "opcode_api")
        assert result[0] == "CONST"
        assert result[1] == "CALL Lcom/Foo;->setX(I)V"
        assert result[2] == "LOAD Lcom/Foo;->name:Ljava/lang/String;"
        assert result[3] == "RET"

    def test_empty_body(self):
        from redb.extractors.decompiler.apk.smali_normalization import normalize_method_body
        assert normalize_method_body("") == []
        assert normalize_method_body("\n\n") == []


class TestNormalizationWithMinHash:
    """Integration tests: normalization + MinHash computation."""

    def test_minhash_returns_signature(self):
        from redb.extractors.decompiler.apk.method_extractor import compute_minhash
        body = """\
const/4 v0, 0x0
invoke-virtual {p0, v0}, Lcom/Foo;->bar(I)V
iget-object v1, p0, Lcom/Foo;->name:Ljava/lang/String;
add-int v2, v0, v1
return-void
"""
        sig = compute_minhash(body)
        assert sig is not None
        assert len(sig) == 64
        assert all(0 <= v < 256 for v in sig)

    def test_minhash_register_invariance(self):
        """Same operations with different registers should produce identical signatures."""
        from redb.extractors.decompiler.apk.method_extractor import compute_minhash
        body_a = """\
const/4 v0, 0x0
invoke-virtual {p0, v0}, Lcom/Foo;->bar(I)V
iget-object v1, p0, Lcom/Foo;->name:Ljava/lang/String;
add-int v2, v0, v1
return-void
"""
        body_b = """\
const/4 v3, 0x0
invoke-virtual {p1, v3}, Lcom/Foo;->bar(I)V
iget-object v4, p1, Lcom/Foo;->name:Ljava/lang/String;
add-int v5, v3, v4
return-void
"""
        sig_a = compute_minhash(body_a)
        sig_b = compute_minhash(body_b)
        assert sig_a == sig_b

    def test_minhash_width_invariance(self):
        """Width variants of same operations should produce identical signatures."""
        from redb.extractors.decompiler.apk.method_extractor import compute_minhash
        body_int = """\
add-int v0, v1, v2
sub-int v3, v0, v1
mul-int v4, v0, v3
return v4
"""
        body_long = """\
add-long v0, v2, v4
sub-long v6, v0, v2
mul-long v8, v0, v6
return-wide v8
"""
        sig_int = compute_minhash(body_int)
        sig_long = compute_minhash(body_long)
        # Both normalize to ALU, ALU, ALU, RET -> identical
        assert sig_int == sig_long

    def test_minhash_different_apis_differ(self):
        """Methods calling different APIs should produce different signatures."""
        from redb.extractors.decompiler.apk.method_extractor import compute_minhash
        body_a = """\
const/4 v0, 0x0
invoke-virtual {p0, v0}, Lcom/crypto/AES;->encrypt([B)[B
invoke-virtual {v0}, Ljava/io/OutputStream;->write([B)V
return-void
"""
        body_b = """\
const/4 v0, 0x0
invoke-virtual {p0, v0}, Ljava/lang/String;->length()I
invoke-virtual {v0}, Ljava/io/PrintStream;->println(I)V
return-void
"""
        sig_a = compute_minhash(body_a)
        sig_b = compute_minhash(body_b)
        assert sig_a != sig_b

    def test_minhash_too_few_instructions(self):
        from redb.extractors.decompiler.apk.method_extractor import compute_minhash
        assert compute_minhash("return-void\nnop") is None


class TestACFGBlockFeatures:
    """Tests for per-block ACFG feature extraction via smali_cfg."""

    def test_simple_linear_method(self):
        from redb.extractors.decompiler.apk.smali_cfg import compute_cfg_metrics
        body = """\
add-int v0, v1, v2
invoke-virtual {p0}, Lcom/Foo;->bar()V
return-void
"""
        metrics = compute_cfg_metrics(body)
        assert metrics.block_count >= 1
        assert len(metrics.block_features) == metrics.block_count
        # Each feature vector has 8 elements
        for feat in metrics.block_features:
            assert len(feat) == 8

    def test_branching_method_has_multiple_blocks(self):
        from redb.extractors.decompiler.apk.smali_cfg import compute_cfg_metrics
        body = """\
const/4 v0, 0x0
if-eqz v0, :cond_0
invoke-virtual {p0}, Lcom/Foo;->a()V
goto :goto_0
:cond_0
invoke-virtual {p0}, Lcom/Foo;->b()V
:goto_0
return-void
"""
        metrics = compute_cfg_metrics(body)
        assert metrics.block_count >= 3
        assert len(metrics.block_features) == metrics.block_count

    def test_feature_vector_arithmetic_count(self):
        from redb.extractors.decompiler.apk.smali_cfg import compute_cfg_metrics
        body = """\
add-int v0, v1, v2
sub-int v3, v0, v1
mul-int v4, v0, v3
return v4
"""
        metrics = compute_cfg_metrics(body)
        assert len(metrics.block_features) == 1
        feat = metrics.block_features[0]
        # feat[0] = instr_count (4), feat[1] = arithmetic (3)
        assert feat[0] == 4
        assert feat[1] >= 3  # 3 ALU ops

    def test_feature_vector_call_count(self):
        from redb.extractors.decompiler.apk.smali_cfg import compute_cfg_metrics
        body = """\
invoke-virtual {p0}, Lcom/A;->a()V
invoke-static {v0}, Lcom/B;->b()V
return-void
"""
        metrics = compute_cfg_metrics(body)
        feat = metrics.block_features[0]
        # feat[4] = call count
        assert feat[4] == 2