Vincent Moulton

172 papers A 1C 7Journal 159Unranked 3
YearRankTypeTitle / Venue / Authors
2026 J jnl
Adv. Appl. Math.
Éva Czabarka, Steven Kelk, Vincent Moulton, László A. Székely
2025 J jnl
Discret. Appl. Math.
David Bryant, Katharina T. Huber, Vincent Moulton, Andreas Spillner
2025 J jnl
Discret. Math. Theor. Comput. Sci.
Katharina T. Huber, Simone Linz, Vincent Moulton
2025 J jnl
CoRR
Anna Lindeberg, Anton Alfonsson, Vincent Moulton, Guillaume E. Scholz, Marc Hellmuth
2025 J jnl
Inf. Process. Lett.
Katharina T. Huber, Leo van Iersel, Vincent Moulton, Guillaume E. Scholz
2025 J jnl
CoRR
Vincent Moulton, Andreas Spillner
2025 J jnl
Theor. Comput. Sci.
Manuel Lafond, Vincent Moulton
2025 J jnl
CoRR
David Bryant, Katharina T. Huber, Vincent Moulton, Andreas Spillner
2024 J jnl
Electron. J. Comb.
Vincent Moulton, Guillaume E. Scholz
2024 J jnl
CoRR
Katharina T. Huber, Leo van Iersel, Mark Jones, Vincent Moulton, Leonie Veenema-Nipius
2024 J jnl
J. Comput. Syst. Sci.
Katharina T. Huber, Leo van Iersel, Remie Janssen, Mark Jones, Vincent Moulton, Yukihiro Murakami, Charles Semple
2024 J jnl
CoRR
Manuel Lafond, Vincent Moulton
2024 J jnl
Electron. J. Comb.
Katharina T. Huber, Simone Linz, Vincent Moulton, Charles Semple
2024 J jnl
SIAM J. Discret. Math.
Katharina T. Huber, Vincent Moulton, Guillaume E. Scholz
2024 J jnl
CoRR
Vincent Moulton, Andreas Spillner
2023 J jnl
J. Graph Algorithms Appl.
Katharina T. Huber, Vincent Moulton, Andreas Spillner
2023 J jnl
Discret. Comput. Geom.
David Bryant, Katharina T. Huber, Vincent Moulton, Paul F. Tupper
2023 J jnl
Adv. Appl. Math.
Andrew R. Francis, Katharina T. Huber, Vincent Moulton, Taoyang Wu
2023 J jnl
Graphs Comb.
Marc Hellmuth, Katharina T. Huber, Vincent Moulton, Guillaume E. Scholz, Peter F. Stadler
2023 J jnl
IEEE ACM Trans. Comput. Biol. Bioinform.
Vincent Moulton, Taoyang Wu
2023 J jnl
Discret. Appl. Math.
Carsten R. Seemann, Vincent Moulton, Peter F. Stadler, Marc Hellmuth
2023 J jnl
Inf. Process. Lett.
Leo van Iersel, Vincent Moulton, Yukihiro Murakami
2023 J jnl
CoRR
Katharina T. Huber, Vincent Moulton, Guillaume E. Scholz
2022 J jnl
Inf. Process. Lett.
Leo van Iersel, Sjors Kole, Vincent Moulton, Leonie Nipius
2022 J jnl
IEEE ACM Trans. Comput. Biol. Bioinform.
Salma Alzahrani, Christopher Applegate, David Swarbreck, Tamas Dalmay, Leighton Folkes, Vincent Moulton
2022 J jnl
Discret. Appl. Math.
Katharina T. Huber, Leo van Iersel, Remie Janssen, Mark Jones, Vincent Moulton, Yukihiro Murakami
2022 J jnl
Order
Vincent Moulton, Andreas Spillner
2022 J jnl
Discret. Appl. Math.
Katharina T. Huber, Vincent Moulton, Guillaume E. Scholz
2022 J jnl
SIAM J. Discret. Math.
Vincent Moulton, Taoyang Wu
2021 J jnl
CoRR
Leo van Iersel, Sjors Kole, Vincent Moulton, Leonie Nipius
2021 J jnl
Discret. Appl. Math.
Katharina T. Huber, Vincent Moulton, Andreas Spillner
2021 J jnl
CoRR
Carsten R. Seemann, Vincent Moulton, Peter F. Stadler, Marc Hellmuth
2020 J jnl
Algorithms Mol. Biol.
Mattéo Delabre, Nadia El-Mabrouk, Katharina T. Huber, Manuel Lafond, Vincent Moulton, Emmanuel Noutahi, Miguel Sautie Castellanos
2020 J jnl
Inf. Process. Lett.
Momoko Hayamizu, Katharina T. Huber, Vincent Moulton, Yukihiro Murakami
2020 J jnl
Adv. Appl. Math.
Leo van Iersel, Vincent Moulton, Yukihiro Murakami
2020 J jnl
CoRR
Katharina T. Huber, Simone Linz, Vincent Moulton
2019 J jnl
J. Classif.
Katharina T. Huber, Vincent Moulton, Taoyang Wu
2019 J jnl
CoRR
Vincent Moulton, Andreas Spillner
2019 J jnl
CoRR
Momoko Hayamizu, Katharina T. Huber, Vincent Moulton, Yukihiro Murakami
2019 J jnl
CoRR
Katharina T. Huber, Leo van Iersel, Remie Janssen, Mark Jones, Vincent Moulton, Yukihiro Murakami, Charles Semple
2019 J jnl
Theor. Comput. Sci.
Manuel Lafond, Nadia El-Mabrouk, Katharina T. Huber, Vincent Moulton
2019 J jnl
Discret. Math.
Katharina T. Huber, Jack H. Koolen, Vincent Moulton
2019 J jnl
J. Classif.
Katharina T. Huber, Vincent Moulton, Guillaume E. Scholz
2018 J jnl
Adv. Appl. Math.
Stefan Grünewald, Katharina T. Huber, Vincent Moulton, Mike A. Steel
2018 J jnl
Inf. Process. Lett.
Katharina T. Huber, Vincent Moulton, Marie-France Sagot, Blerina Sinaimeri
2018 J jnl
SIAM J. Discret. Math.
Leo van Iersel, Vincent Moulton
2018 conf
RECOMB-CG
Mattéo Delabre, Nadia El-Mabrouk, Katharina T. Huber, Manuel Lafond, Vincent Moulton, Emmanuel Noutahi, Miguel Sautie Castellanos
2018 J jnl
Bioinform.
Sarah Bastkowski, Daniel Mapleson, Andreas Spillner, Taoyang Wu, Monika Balvociute, Vincent Moulton
2018 J jnl
Bioinform.
Matthew B. Stocks, Irina Mohorianu, Matthew Beckers, Claudia Paicu, Simon Moxon, Joshua Thody, Tamas Dalmay, Vincent Moulton
2018 J jnl
CoRR
Manuel Lafond, Nadia El-Mabrouk, Katharina T. Huber, Vincent Moulton
2018 J jnl
Theor. Comput. Sci.
Vincent Moulton, Andreas Spillner, Taoyang Wu
2017 J jnl
CoRR
Vincent Moulton, James Oldman, Taoyang Wu
2017 J jnl
Inf. Process. Lett.
Vincent Moulton, James Oldman, Taoyang Wu
2017 J jnl
CoRR
Leo van Iersel, Vincent Moulton, Eveline de Swart, Taoyang Wu
2017 J jnl
CoRR
Maribel Hernandez-Rosales, Marc Hellmuth, Nicolas Wieseke, Katharina T. Huber, Vincent Moulton, Peter F. Stadler
2017 J jnl
CoRR
Katharina T. Huber, Vincent Moulton, Charles Semple, Taoyang Wu
2017 J jnl
Algorithmica
Katharina T. Huber, Leo van Iersel, Vincent Moulton, Céline Scornavacca, Taoyang Wu
2017 J jnl
CoRR
Magnus Bordewich, Katharina T. Huber, Vincent Moulton, Charles Semple
2017 J jnl
CoRR
Andrew R. Francis, Katharina T. Huber, Vincent Moulton
2017 J jnl
CoRR
Vincent Moulton, Andreas Spillner, Taoyang Wu
2017 J jnl
Bioinform.
Claudia Paicu, Irina Mohorianu, Matthew B. Stocks, Ping Xu, Aurore Coince, Martina Billmeier, Tamas Dalmay, Vincent Moulton, Simon Moxon
2016 J jnl
Australas. J Comb.
Andreas W. M. Dress, Katharina T. Huber, Jacobus H. Koolen, Vincent Moulton, Andreas Spillner
2016 J jnl
Theor. Comput. Sci.
Steven Kelk, Mareike Fischer, Vincent Moulton, Taoyang Wu
2016 J jnl
Bioinform.
Sarah Bastkowski, Vincent Moulton, Andreas Spillner, Taoyang Wu
2015 J jnl
Adv. Appl. Math.
Vincent Moulton, Taoyang Wu
2015 J jnl
Discret. Math.
Sven Herrmann, Jack H. Koolen, Alice Lesser, Vincent Moulton, Taoyang Wu
2015 J jnl
CoRR
Steven Kelk, Mareike Fischer, Vincent Moulton, Taoyang Wu
2014 J jnl
Discret. Appl. Math.
Sven Herrmann, Vincent Moulton
2014 J jnl
Inf. Process. Lett.
Sarah Bastkowski, Andreas Spillner, Vincent Moulton
2014 J jnl
CoRR
Katharina T. Huber, Leo van Iersel, Vincent Moulton, Céline Scornavacca, Taoyang Wu
2014 J jnl
SIAM J. Discret. Math.
Katharina T. Huber, Vincent Moulton, Charles Semple, Taoyang Wu
2013 J jnl
Algorithmica
Katharina T. Huber, Vincent Moulton
2013 J jnl
Discret. Appl. Math.
Éva Czabarka, Péter L. Erdös, Virginia Johnson, Vincent Moulton
2013 J jnl
Discret. Appl. Math.
Andreas W. M. Dress, Vincent Moulton, Andreas Spillner, Taoyang Wu
2013 J jnl
Discret. Optim.
Sven Herrmann, Vincent Moulton, Andreas Spillner
2013 J jnl
IEEE ACM Trans. Comput. Biol. Bioinform.
Stefan Grünewald, Andreas Spillner, Sarah Bastkowski, Anja Bögershausen, Vincent Moulton
2012 book
Andreas W. M. Dress, Katharina T. Huber, Jacobus H. Koolen, Vincent Moulton, Andreas Spillner
2012 J jnl
Artif. Life
Jan T. Kim, Anyela Camargo, Alessandra Devoto, Vincent Moulton, John Turner
2012 A conf
ALENEX
Katharina T. Huber, Vincent Moulton, Andreas Spillner, Sabine Storandt, Radoslaw Suchecki
2012 J jnl
Algorithms Mol. Biol.
Iain Martyn, Tyler S. Kuhn, Arne O. Mooers, Vincent Moulton, Andreas Spillner
2012 J jnl
CoRR
Sven Herrmann, Vincent Moulton
2012 J jnl
IEEE ACM Trans. Comput. Biol. Bioinform.
Andreas Spillner, Binh T. Nguyen, Vincent Moulton
2012 J jnl
WIREs Data Mining Knowl. Discov.
Irina Mohorianu, Sara Lopez-Gomollon, Frank Schwach, Tamas Dalmay, Vincent Moulton
2012 J jnl
BMC Bioinform.
Maribel Hernandez-Rosales, Marc Hellmuth, Nicolas Wieseke, Katharina T. Huber, Vincent Moulton, Peter F. Stadler
2012 J jnl
Discret. Math.
Jack H. Koolen, Alice Lesser, Vincent Moulton, Taoyang Wu
2012 J jnl
J. Classif.
Sven Herrmann, Katharina T. Huber, Vincent Moulton, Andreas Spillner
2012 J jnl
Bioinform.
Matthew B. Stocks, Simon Moxon, Daniel Mapleson, Hugh C. Woolfenden, Irina Mohorianu, Leighton Folkes, Frank Schwach, Tamas Dalmay, Vincent Moulton
2012 J jnl
Adv. Appl. Math.
Sven Herrmann, Vincent Moulton
2012 J jnl
Discret. Math.
Sven Herrmann, Vincent Moulton
2011 J jnl
SIAM J. Discret. Math.
Andreas W. M. Dress, Katharina T. Huber, Jacobus H. Koolen, Vincent Moulton
2011 J jnl
CoRR
Katharina T. Huber, Vincent Moulton
2011 J jnl
J. Bioinform. Comput. Biol.
Marc Thuillard, Vincent Moulton
2011 J jnl
IEEE ACM Trans. Comput. Biol. Bioinform.
Katharina T. Huber, Andreas Spillner, Radoslaw Suchecki, Vincent Moulton
2011 J jnl
CoRR
Vincent Moulton, Mike A. Steel
2010 C ed.
WABI
Vincent Moulton, Mona Singh
2010 J jnl
J. Classif.
Andreas W. M. Dress, Katharina T. Huber, Jacobus H. Koolen, Vincent Moulton, Andreas Spillner
2010 J jnl
Algorithms Mol. Biol.
Binh T. Nguyen, Andreas Spillner, Brent C. Emerson, Vincent Moulton
2010 J jnl
BMC Bioinform.
Daniel MacLean, Vincent Moulton, David J. Studholme
2010 J jnl
Bioinform.
Darren P. Martin, Philippe Lemey, Martin Lott, Vincent Moulton, David Posada, Pierre Lefeuvre
2009 J jnl
Appl. Math. Lett.
Andreas W. M. Dress, Katharina T. Huber, Jacobus H. Koolen, Vincent Moulton
2009 J jnl
CoRR
Andreas W. M. Dress, Katharina T. Huber, Jacobus H. Koolen, Vincent Moulton, Andreas Spillner
2009 J jnl
Appl. Math. Lett.
Andreas W. M. Dress, Vincent Moulton, Andreas Spillner, Taoyang Wu
2009 J jnl
Adv. Appl. Math.
Stefan Grünewald, Katharina T. Huber, Vincent Moulton, Charles Semple, Andreas Spillner
2009 J jnl
IEEE ACM Trans. Comput. Biol. Bioinform.
Magnus Bordewich, Olivier Gascuel, Katharina T. Huber, Vincent Moulton
2009 J jnl
Discret. Appl. Math.
Stefan Grünewald, Vincent Moulton, Andreas Spillner
2009 J jnl
BMC Bioinform.
Philippe Lemey, Martin Lott, Darren P. Martin, Vincent Moulton
2009 J jnl
IEEE ACM Trans. Comput. Biol. Bioinform.
Stefan Grünewald, Vincent Moulton
2009 J jnl
Eur. J. Comb.
Jack H. Koolen, Alice Lesser, Vincent Moulton
2009 J jnl
Bioinform.
Martin Lott, Andreas Spillner, Katharina T. Huber, Vincent Moulton
2009 J jnl
Appl. Math. Lett.
Vincent Moulton, Andreas Spillner
2009 J jnl
IEEE ACM Trans. Comput. Biol. Bioinform.
Taoyang Wu, Vincent Moulton, Mike A. Steel
2009 J jnl
IEEE ACM Trans. Comput. Biol. Bioinform.
Daniel H. Huson, Vincent Moulton, Mike A. Steel
2008 J jnl
Adv. Appl. Math.
Jonna Gill, Svante Linusson, Vincent Moulton, Mike A. Steel
2008 J jnl
Algorithms Mol. Biol.
Simon Moxon, Vincent Moulton, Jan T. Kim
2008 J jnl
Bioinform.
Simon Moxon, Frank Schwach, Tamas Dalmay, Daniel MacLean, David J. Studholme, Vincent Moulton
2008 J jnl
Appl. Math. Lett.
Andreas W. M. Dress, Katharina T. Huber, Jacobus H. Koolen, Vincent Moulton
2008 J jnl
Electron. J. Comb.
Katharina T. Huber, Jacobus H. Koolen, Vincent Moulton, Andreas Spillner
2008 J jnl
Eur. J. Comb.
Andreas W. M. Dress, Katharina T. Huber, Jacobus H. Koolen, Vincent Moulton
2008 J jnl
IEEE ACM Trans. Comput. Biol. Bioinform.
Andreas Spillner, Binh T. Nguyen, Vincent Moulton
2008 C conf
WABI
Stefan Grünewald, Andreas Spillner, Kristoffer Forslund, Vincent Moulton
2008 J jnl
Eur. J. Comb.
Y. B. Choe, Katharina T. Huber, Jacobus H. Koolen, Y. S. Kwon, Vincent Moulton
2008 J jnl
Appl. Math. Lett.
Andreas W. M. Dress, Katharina T. Huber, Jacobus H. Koolen, Vincent Moulton
2008 J jnl
J. Comput. Biol.
Katharina T. Huber, Martin Lott, Vincent Moulton, Andreas Spillner
2008 conf
BIOCOMP
Anthony J. Bagnall, Simon Moxon, David J. Studholme, Vincent Moulton
2007 C conf
COCOA
Andreas W. M. Dress, Katharina T. Huber, Jacobus H. Koolen, Vincent Moulton
2007 J jnl
Bioinform.
Eva Freyhult, Vincent Moulton, Peter Clote
2007 J jnl
Discret. Comput. Geom.
Jack H. Koolen, Alice Lesser, Vincent Moulton
2007 J jnl
Algorithms Mol. Biol.
David Bryant, Vincent Moulton, Andreas Spillner
2007 ch.
Mathematics of Evolution and Phylogeny
Vincent Moulton, Katharina T. Huber
2007 J jnl
Nucleic Acids Res.
Eva Freyhult, Vincent Moulton, Peter Clote
2007 J jnl
Eur. J. Comb.
Sejeong Bang, Jacobus H. Koolen, Vincent Moulton
2006 C conf
WABI
Barbara R. Holland, Glenn Conner, Katharina T. Huber, Vincent Moulton
2006 J jnl
Eur. J. Comb.
Katharina T. Huber, Jacobus H. Koolen, Vincent Moulton
2006 J jnl
Reliab. Comput.
Warwick Tucker, Vincent Moulton
2005 J jnl
BMC Bioinform.
Eva Freyhult, Paul P. Gardner, Vincent Moulton
2005 J jnl
Discret. Appl. Math.
Andreas W. M. Dress, Barbara R. Holland, Katharina T. Huber, Jack H. Koolen, Vincent Moulton, Jan Weyer-Menkhoff
2005 J jnl
SIAM J. Discret. Math.
Katharina T. Huber, Vincent Moulton, Michael Anthony Steel
2005 C conf
WABI
Warwick Tucker, Vincent Moulton
2005 J jnl
Appl. Math. Lett.
Katharina T. Huber, Vincent Moulton, Charles Semple, Mike A. Steel
2005 J jnl
Discret. Math.
Katharina T. Huber, Jacobus H. Koolen, Vincent Moulton
2005 J jnl
BMC Bioinform.
Eva Freyhult, Peteris Prusis, Maris Lapinsh, Jarl E. S. Wikberg, Vincent Moulton, Mats G. Gustafsson
2004 J jnl
Adv. Appl. Math.
Vincent Moulton, Mike A. Steel
2004 J jnl
Discret. Appl. Math.
Katharina T. Huber, Vincent Moulton, Charles Semple
2004 J jnl
Discret. Comput. Geom.
Katharina T. Huber, Jacobus H. Koolen, Vincent Moulton
2004 J jnl
Eur. J. Comb.
Jack H. Koolen, Vincent Moulton, Dragan Stevanovic
2004 J jnl
Bioinform.
Kristoffer Forslund, Daniel H. Huson, Vincent Moulton
2003 J jnl
Bioinform.
Sverker Edvardsson, Paul P. Gardner, Anthony M. Poole, Michael D. Hendy, David Penny, Vincent Moulton
2003 J jnl
Eur. J. Comb.
Sejeong Bang, Jack H. Koolen, Vincent Moulton
2003 J jnl
Eur. J. Comb.
Andreas W. M. Dress, Jack H. Koolen, Vincent Moulton
2003 J jnl
Appl. Math. Lett.
Dietmar Cieslik, Andreas W. M. Dress, Katharina T. Huber, Vincent Moulton
2003 C conf
WABI
Barbara R. Holland, Vincent Moulton
2003 J jnl
Graphs Comb.
Jack H. Koolen, Vincent Moulton
2002 J jnl
Eur. J. Comb.
Andreas W. M. Dress, Katharina T. Huber, Vincent Moulton
2002 J jnl
Eur. J. Comb.
Jack H. Koolen, Vincent Moulton
2002 C conf
WABI
David Bryant, Vincent Moulton
2002 J jnl
Eur. J. Comb.
Andreas W. M. Dress, Jack H. Koolen, Vincent Moulton
2002 J jnl
Eur. J. Comb.
Jack H. Koolen, Vincent Moulton
2002 J jnl
Discret. Appl. Math.
Hans-Jürgen Bandelt, Katharina T. Huber, Vincent Moulton
2002 J jnl
Discret. Math.
Katharina T. Huber, Vincent Moulton
2001 J jnl
Adv. Appl. Math.
Jack H. Koolen, Vincent Moulton
2001 J jnl
Eur. J. Comb.
Andreas W. M. Dress, Katharina T. Huber, Jacobus H. Koolen, Vincent Moulton
2000 J jnl
Eur. J. Comb.
Jack H. Koolen, Vincent Moulton, Udo Tönges
2000 J jnl
Discret. Comput. Geom.
Andreas W. M. Dress, Katharina T. Huber, Vincent Moulton
2000 J jnl
J. Comput. Biol.
Vincent Moulton, Michael Zuker, Mike A. Steel, Robin Pointon, David Penny
2000 J jnl
Discret. Math.
Oliver Goodman, Vincent Moulton
1999 J jnl
Discret. Appl. Math.
Vincent Moulton, Mike A. Steel
1999 J jnl
Electron. Notes Discret. Math.
Andreas W. M. Dress, Katharina T. Huber, Vincent Moulton
1998 J jnl
Discret. Math.
Jack H. Koolen, Vincent Moulton, Udo Tönges
1997 J jnl
Comput. Appl. Biosci.
Jens Stoye, Vincent Moulton, Andreas W. M. Dress
1996 J jnl
Discret. Appl. Math.
Andreas W. M. Dress, Daniel H. Huson, Vincent Moulton
1996 conf
Mathematical Hierarchies and Biology
Vincent Moulton, Michael Anthony Steel, Chris Tuffley
1996 J jnl
Eur. J. Comb.
Andreas W. M. Dress, Vincent Moulton, Werner F. Terhalle
tests/unit/test_apk_process_method.py
← Index tests/unit/test_apk_process_method.py python
"""Mock-based tests for APKCodeAnalyzer._process_method and androguard integration.

Tests the boundary between our code and androguard objects, covering:
- xref extraction (get_xref_from / get_xref_to) for both 4.x and 3.x APIs
- external method skipping
- library method filtering
- smali lookup and androguard fallback disassembly
- deduplication of content hashes
- full output population (all result dict keys)
- minimum instruction filtering
"""
from dataclasses import dataclass
from typing import List
from unittest.mock import MagicMock, Mock, patch, PropertyMock

import pytest

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


# ---------------------------------------------------------------------------
# Helpers: build mock androguard objects
# ---------------------------------------------------------------------------

SAMPLE_SMALI_BODY = """\
.locals 2
const/4 v0, 0x0
invoke-virtual {p0, v0}, Lcom/example/Foo;->bar(I)V
iget-object v1, p0, Lcom/example/Foo;->name:Ljava/lang/String;
add-int v0, v0, v1
return-void
"""

SAMPLE_JAVA_SOURCE = """\
public void doStuff() {
    int x = 0;
    bar(x);
    String n = this.name;
    x = x + n;
}
"""


def _make_encoded_method(
    class_name="Lcom/example/Foo;",
    method_name="doStuff",
    descriptor="()V",
    code=None,
):
    """Create a mock EncodedMethod."""
    enc = MagicMock()
    enc.get_class_name.return_value = class_name
    enc.get_name.return_value = method_name
    enc.get_descriptor.return_value = descriptor
    enc.get_code.return_value = code
    return enc


def _make_xref_entry_4x(class_name, method_name, offset=0):
    """Create a mock xref tuple in androguard 4.x format.

    4.x: (ClassAnalysis, MethodAnalysis, offset)
    MethodAnalysis has .get_method() returning the EncodedMethod.
    """
    ref_class = MagicMock()
    ref_method = MagicMock()
    inner_enc = MagicMock()
    inner_enc.get_class_name.return_value = class_name
    inner_enc.get_name.return_value = method_name
    ref_method.get_method.return_value = inner_enc
    # Remove direct get_class_name/get_name to simulate 4.x behavior
    del ref_method.get_class_name
    del ref_method.get_name
    return (ref_class, ref_method, offset)


def _make_xref_entry_3x(class_name, method_name, offset=0):
    """Create a mock xref tuple in androguard 3.x format.

    3.x: (ClassAnalysis, EncodedMethod, offset)
    EncodedMethod has .get_class_name() and .get_name() directly.
    """
    ref_class = MagicMock()
    ref_method = MagicMock(spec=[
        "get_class_name", "get_name",
    ])
    ref_method.get_class_name.return_value = class_name
    ref_method.get_name.return_value = method_name
    return (ref_class, ref_method, offset)


def _make_method_analysis(
    encoded=None,
    is_external=False,
    xref_from=None,
    xref_to=None,
):
    """Create a mock MethodAnalysis."""
    method = MagicMock()
    method.is_external.return_value = is_external
    method.get_method.return_value = encoded or _make_encoded_method()
    method.get_xref_from.return_value = xref_from or []
    method.get_xref_to.return_value = xref_to or []
    return method


def _make_analyzer(min_instructions=1):
    """Create an APKCodeAnalyzer with mocked dependencies."""
    with patch(
        "redb.extractors.decompiler.apk.analyzer.JADXDecompiler"
    ), patch(
        "redb.extractors.decompiler.apk.analyzer.ApktoolDisassembler"
    ):
        from redb.extractors.decompiler.apk.analyzer import APKCodeAnalyzer
        analyzer = APKCodeAnalyzer("/fake/path.apk")
        analyzer.min_instructions = min_instructions
        analyzer.library_filter = MagicMock()
        analyzer.library_filter.is_library.return_value = False
        return analyzer


def _empty_results():
    """Create a fresh empty results dict."""
    return {
        "decompiled_content": [],
        "decompiled_refs": [],
        "smali_content": [],
        "smali_refs": [],
        "similarity_metrics": [],
        "cfg": [],
        "analysis_errors": [],
    }


def _make_smali_method(body=SAMPLE_SMALI_BODY, instruction_count=5):
    """Create a mock SmaliMethod."""
    m = MagicMock()
    m.body = body
    m.instruction_count = instruction_count
    m.register_count = 2
    return m


# ---------------------------------------------------------------------------
# Tests: xref extraction
# ---------------------------------------------------------------------------

class TestXrefExtraction:
    """Tests that xrefs are correctly extracted from androguard objects."""

    def test_xref_from_androguard_4x(self):
        """4.x: ref_method.get_method().get_class_name() path."""
        analyzer = _make_analyzer()
        results = _empty_results()
        method_key = "Lcom/example/Foo;->doStuff()V"

        xref_from = [
            _make_xref_entry_4x("Lcom/example/Caller;", "init", 42),
            _make_xref_entry_4x("Lcom/example/Other;", "run", 100),
        ]
        method = _make_method_analysis(xref_from=xref_from)

        smali_methods = {method_key: _make_smali_method()}
        java_methods = {"com.example.Foo.doStuff": SAMPLE_JAVA_SOURCE}

        analyzer._process_method(
            method, smali_methods, java_methods,
            results, set(), set(),
        )

        assert len(results["decompiled_refs"]) == 1
        ref = results["decompiled_refs"][0]
        assert ref["functions_caller"] == [
            "Lcom/example/Caller;->init",
            "Lcom/example/Other;->run",
        ]

    def test_xref_to_androguard_4x(self):
        """4.x: callees extracted via get_method() unwrap."""
        analyzer = _make_analyzer()
        results = _empty_results()
        method_key = "Lcom/example/Foo;->doStuff()V"

        xref_to = [
            _make_xref_entry_4x("Lcom/example/Dep;", "calculate", 10),
        ]
        method = _make_method_analysis(xref_to=xref_to)

        smali_methods = {method_key: _make_smali_method()}
        java_methods = {"com.example.Foo.doStuff": SAMPLE_JAVA_SOURCE}

        analyzer._process_method(
            method, smali_methods, java_methods,
            results, set(), set(),
        )

        ref = results["decompiled_refs"][0]
        assert ref["functions_call"] == ["Lcom/example/Dep;->calculate"]

    def test_xref_from_androguard_3x_fallback(self):
        """3.x: ref_method.get_class_name() direct access fallback."""
        analyzer = _make_analyzer()
        results = _empty_results()
        method_key = "Lcom/example/Foo;->doStuff()V"

        xref_from = [
            _make_xref_entry_3x("Lcom/example/OldCaller;", "legacy"),
        ]
        method = _make_method_analysis(xref_from=xref_from)

        smali_methods = {method_key: _make_smali_method()}
        java_methods = {"com.example.Foo.doStuff": SAMPLE_JAVA_SOURCE}

        analyzer._process_method(
            method, smali_methods, java_methods,
            results, set(), set(),
        )

        ref = results["decompiled_refs"][0]
        assert ref["functions_caller"] == ["Lcom/example/OldCaller;->legacy"]

    def test_xref_to_androguard_3x_fallback(self):
        """3.x: callees via direct access fallback."""
        analyzer = _make_analyzer()
        results = _empty_results()
        method_key = "Lcom/example/Foo;->doStuff()V"

        xref_to = [
            _make_xref_entry_3x("Lcom/example/OldDep;", "compute"),
        ]
        method = _make_method_analysis(xref_to=xref_to)

        smali_methods = {method_key: _make_smali_method()}
        java_methods = {"com.example.Foo.doStuff": SAMPLE_JAVA_SOURCE}

        analyzer._process_method(
            method, smali_methods, java_methods,
            results, set(), set(),
        )

        ref = results["decompiled_refs"][0]
        assert ref["functions_call"] == ["Lcom/example/OldDep;->compute"]

    def test_empty_xrefs(self):
        """No xrefs produces empty lists, not None."""
        analyzer = _make_analyzer()
        results = _empty_results()
        method_key = "Lcom/example/Foo;->doStuff()V"

        method = _make_method_analysis()
        smali_methods = {method_key: _make_smali_method()}
        java_methods = {"com.example.Foo.doStuff": SAMPLE_JAVA_SOURCE}

        analyzer._process_method(
            method, smali_methods, java_methods,
            results, set(), set(),
        )

        ref = results["decompiled_refs"][0]
        assert ref["functions_caller"] == []
        assert ref["functions_call"] == []

    def test_xref_exception_does_not_crash(self):
        """If get_xref_from() itself throws, method still processes."""
        analyzer = _make_analyzer()
        results = _empty_results()
        method_key = "Lcom/example/Foo;->doStuff()V"

        method = _make_method_analysis()
        method.get_xref_from.side_effect = RuntimeError("xref table corrupt")
        method.get_xref_to.side_effect = RuntimeError("xref table corrupt")

        smali_methods = {method_key: _make_smali_method()}
        java_methods = {"com.example.Foo.doStuff": SAMPLE_JAVA_SOURCE}

        analyzer._process_method(
            method, smali_methods, java_methods,
            results, set(), set(),
        )

        # Method should still be processed with empty xrefs
        assert len(results["smali_refs"]) == 1
        ref = results["decompiled_refs"][0]
        assert ref["functions_caller"] == []
        assert ref["functions_call"] == []

    def test_multiple_xrefs(self):
        """Multiple callers and callees are all captured."""
        analyzer = _make_analyzer()
        results = _empty_results()
        method_key = "Lcom/example/Foo;->doStuff()V"

        xref_from = [
            _make_xref_entry_4x("Lcom/A;", "a1"),
            _make_xref_entry_4x("Lcom/B;", "b1"),
            _make_xref_entry_4x("Lcom/C;", "c1"),
        ]
        xref_to = [
            _make_xref_entry_4x("Lcom/D;", "d1"),
            _make_xref_entry_4x("Lcom/E;", "e1"),
        ]
        method = _make_method_analysis(xref_from=xref_from, xref_to=xref_to)

        smali_methods = {method_key: _make_smali_method()}
        java_methods = {"com.example.Foo.doStuff": SAMPLE_JAVA_SOURCE}

        analyzer._process_method(
            method, smali_methods, java_methods,
            results, set(), set(),
        )

        ref = results["decompiled_refs"][0]
        assert len(ref["functions_caller"]) == 3
        assert len(ref["functions_call"]) == 2


# ---------------------------------------------------------------------------
# Tests: method filtering and skipping
# ---------------------------------------------------------------------------

class TestMethodFiltering:
    """Tests for external/library/min-instruction filtering."""

    def test_external_method_skipped(self):
        """External methods (no code body) produce no output."""
        analyzer = _make_analyzer()
        results = _empty_results()

        method = _make_method_analysis(is_external=True)

        analyzer._process_method(
            method, {}, {}, results, set(), set(),
        )

        assert all(len(v) == 0 for v in results.values())

    def test_library_method_skipped(self):
        """Library methods produce no output."""
        analyzer = _make_analyzer()
        analyzer.library_filter.is_library.return_value = True
        results = _empty_results()

        method = _make_method_analysis()

        analyzer._process_method(
            method, {}, {}, results, set(), set(),
        )

        assert all(len(v) == 0 for v in results.values())

    def test_min_instruction_filter(self):
        """Methods below min_instructions threshold are skipped."""
        analyzer = _make_analyzer(min_instructions=10)
        results = _empty_results()
        method_key = "Lcom/example/Foo;->doStuff()V"

        method = _make_method_analysis()
        # Only 3 instructions — below threshold of 10
        smali_methods = {method_key: _make_smali_method(instruction_count=3)}

        analyzer._process_method(
            method, smali_methods, {}, results, set(), set(),
        )

        assert all(len(v) == 0 for v in results.values())

    def test_method_above_min_instructions_processed(self):
        """Methods at or above threshold are processed."""
        analyzer = _make_analyzer(min_instructions=5)
        results = _empty_results()
        method_key = "Lcom/example/Foo;->doStuff()V"

        method = _make_method_analysis()
        smali_methods = {method_key: _make_smali_method(instruction_count=5)}

        analyzer._process_method(
            method, smali_methods, {}, results, set(), set(),
        )

        assert len(results["smali_content"]) == 1
        assert len(results["smali_refs"]) == 1


# ---------------------------------------------------------------------------
# Tests: smali body lookup and androguard fallback
# ---------------------------------------------------------------------------

class TestSmaliLookup:
    """Tests for smali body lookup from apktool and androguard fallback."""

    def test_apktool_smali_used_when_available(self):
        """When apktool smali is available, it's used."""
        analyzer = _make_analyzer()
        results = _empty_results()
        method_key = "Lcom/example/Foo;->doStuff()V"

        method = _make_method_analysis()
        smali_methods = {method_key: _make_smali_method()}

        analyzer._process_method(
            method, smali_methods, {}, results, set(), set(),
        )

        assert len(results["smali_content"]) == 1
        assert results["smali_content"][0]["smali_method"] == SAMPLE_SMALI_BODY

    def test_androguard_fallback_when_no_apktool(self):
        """When apktool smali is missing, _disassemble_with_androguard is used."""
        analyzer = _make_analyzer()
        results = _empty_results()

        # Create encoded method with code
        instruction1 = MagicMock()
        instruction1.get_name.return_value = "const/4"
        instruction1.get_output.return_value = "v0, 0x0"
        instruction2 = MagicMock()
        instruction2.get_name.return_value = "invoke-virtual"
        instruction2.get_output.return_value = "v0, Lcom/Foo;->bar()V"
        instruction3 = MagicMock()
        instruction3.get_name.return_value = "add-int"
        instruction3.get_output.return_value = "v0, v1, v2"
        instruction4 = MagicMock()
        instruction4.get_name.return_value = "sub-int"
        instruction4.get_output.return_value = "v3, v0, v1"
        instruction5 = MagicMock()
        instruction5.get_name.return_value = "return-void"
        instruction5.get_output.return_value = ""

        code = MagicMock()
        code.get_registers_size.return_value = 4
        bytecode = MagicMock()
        bytecode.get_instructions.return_value = [
            instruction1, instruction2, instruction3, instruction4, instruction5,
        ]
        code.get_bc.return_value = bytecode

        encoded = _make_encoded_method(code=code)
        method = _make_method_analysis(encoded=encoded)

        # Empty smali_methods — forces androguard fallback
        analyzer._process_method(
            method, {}, {}, results, set(), set(),
        )

        assert len(results["smali_content"]) == 1
        body = results["smali_content"][0]["smali_method"]
        assert "const/4" in body
        assert "return-void" in body

    def test_androguard_fallback_no_code(self):
        """When encoded method has no code, method is skipped."""
        analyzer = _make_analyzer()
        results = _empty_results()

        encoded = _make_encoded_method(code=None)
        method = _make_method_analysis(encoded=encoded)

        analyzer._process_method(
            method, {}, {}, results, set(), set(),
        )

        assert all(len(v) == 0 for v in results.values())

    def test_androguard_fallback_bytecode_exception(self):
        """When bytecode iteration throws, method is skipped gracefully."""
        analyzer = _make_analyzer()
        results = _empty_results()

        code = MagicMock()
        code.get_registers_size.return_value = 2
        bytecode = MagicMock()
        bytecode.get_instructions.side_effect = RuntimeError("bad bytecode")
        code.get_bc.return_value = bytecode

        encoded = _make_encoded_method(code=code)
        method = _make_method_analysis(encoded=encoded)

        analyzer._process_method(
            method, {}, {}, results, set(), set(),
        )

        # Should not crash, but no content produced
        assert len(results["smali_content"]) == 0


# ---------------------------------------------------------------------------
# Tests: output population and deduplication
# ---------------------------------------------------------------------------

class TestOutputPopulation:
    """Tests that all result dict keys are correctly populated."""

    def _process_standard_method(self, with_java=True):
        """Helper: process a standard method and return results."""
        analyzer = _make_analyzer()
        results = _empty_results()
        method_key = "Lcom/example/Foo;->doStuff()V"

        xref_from = [_make_xref_entry_4x("Lcom/Caller;", "call")]
        xref_to = [_make_xref_entry_4x("Lcom/Dep;", "dep")]
        method = _make_method_analysis(
            xref_from=xref_from, xref_to=xref_to
        )

        smali_methods = {method_key: _make_smali_method()}
        java_methods = {}
        if with_java:
            java_methods = {"com.example.Foo.doStuff": SAMPLE_JAVA_SOURCE}

        analyzer._process_method(
            method, smali_methods, java_methods,
            results, set(), set(),
        )
        return results

    def test_smali_content_populated(self):
        results = self._process_standard_method()
        assert len(results["smali_content"]) == 1
        entry = results["smali_content"][0]
        assert "smali_method_hash" in entry
        assert entry["smali_method"] == SAMPLE_SMALI_BODY
        assert entry["smali_method_type"] == "USER"
        assert entry["smali_instructions_count"] == 5
        assert isinstance(entry["smali_has_string_encryption"], bool)
        assert isinstance(entry["smali_has_reflection_calls"], bool)
        assert isinstance(entry["smali_excessive_goto_count"], bool)

    def test_smali_refs_populated(self):
        results = self._process_standard_method()
        assert len(results["smali_refs"]) == 1
        ref = results["smali_refs"][0]
        assert "smali_method_hash" in ref
        assert ref["smali_class_name"] == "com.example.Foo"
        assert ref["smali_method_name"] == "doStuff"
        assert ref["smali_method_signature"] == "()V"

    def test_decompiled_content_populated_with_java(self):
        results = self._process_standard_method(with_java=True)
        assert len(results["decompiled_content"]) == 1
        entry = results["decompiled_content"][0]
        assert "decompiled_method_hash" in entry
        assert entry["decompiled_method"] == SAMPLE_JAVA_SOURCE

    def test_decompiled_refs_populated_with_java(self):
        results = self._process_standard_method(with_java=True)
        assert len(results["decompiled_refs"]) == 1
        ref = results["decompiled_refs"][0]
        assert "decompiled_method_hash" in ref
        assert "smali_method_hash" in ref
        assert ref["decompiled_class_name"] == "com.example.Foo"
        assert ref["decompiled_method_name"] == "doStuff"
        assert "decompiled_method_prototype" in ref
        assert ref["functions_caller"] == ["Lcom/Caller;->call"]
        assert ref["functions_call"] == ["Lcom/Dep;->dep"]

    def test_no_decompiled_without_java(self):
        results = self._process_standard_method(with_java=False)
        assert len(results["decompiled_content"]) == 0
        assert len(results["decompiled_refs"]) == 0
        # Smali should still be there
        assert len(results["smali_content"]) == 1

    def test_similarity_metrics_populated(self):
        results = self._process_standard_method()
        assert len(results["similarity_metrics"]) == 1
        sim = results["similarity_metrics"][0]
        assert "smali_method_hash" in sim
        assert "cyclomatic_complexity" in sim
        assert "minhash" in sim
        assert isinstance(sim["minhash"], list)
        # block_count moved to cfg table
        assert "block_count" not in sim

    def test_cfg_entry_populated(self):
        results = self._process_standard_method()
        assert len(results["cfg"]) == 1
        cfg = results["cfg"][0]
        assert "smali_method_hash" in cfg
        assert "block_count" in cfg
        assert "cfg_topology_hash" in cfg
        assert "prime_product_smali" in cfg
        assert "wl_minhash" in cfg
        assert "bb_features" in cfg
        assert isinstance(cfg["bb_features"], list)
        for feat in cfg["bb_features"]:
            assert len(feat) == 8

    def test_smali_deduplication(self):
        """Same smali body processed twice produces one content entry."""
        analyzer = _make_analyzer()
        results = _empty_results()
        method_key = "Lcom/example/Foo;->doStuff()V"
        seen_smali = set()

        method = _make_method_analysis()
        smali_methods = {method_key: _make_smali_method()}

        # Process same method twice
        analyzer._process_method(
            method, smali_methods, {}, results, set(), seen_smali,
        )
        analyzer._process_method(
            method, smali_methods, {}, results, set(), seen_smali,
        )

        # Content deduplicated, but refs are per-call
        assert len(results["smali_content"]) == 1
        assert len(results["smali_refs"]) == 2

    def test_decompiled_deduplication(self):
        """Same Java source processed twice produces one content entry."""
        analyzer = _make_analyzer()
        results = _empty_results()
        method_key = "Lcom/example/Foo;->doStuff()V"
        seen_decompiled = set()
        seen_smali = set()

        method = _make_method_analysis()
        smali_methods = {method_key: _make_smali_method()}
        java_methods = {"com.example.Foo.doStuff": SAMPLE_JAVA_SOURCE}

        analyzer._process_method(
            method, smali_methods, java_methods,
            results, seen_decompiled, seen_smali,
        )
        analyzer._process_method(
            method, smali_methods, java_methods,
            results, seen_decompiled, seen_smali,
        )

        assert len(results["decompiled_content"]) == 1
        assert len(results["decompiled_refs"]) == 2


# ---------------------------------------------------------------------------
# Tests: _disassemble_with_androguard
# ---------------------------------------------------------------------------

class TestDisassembleWithAndroguard:
    """Tests for the androguard bytecode disassembly fallback."""

    def test_no_code_returns_none(self):
        from redb.extractors.decompiler.apk.analyzer import APKCodeAnalyzer
        encoded = _make_encoded_method(code=None)
        body, count, regs = APKCodeAnalyzer._disassemble_with_androguard(encoded)
        assert body is None
        assert count == 0
        assert regs == 0

    def test_no_bytecode_returns_none(self):
        from redb.extractors.decompiler.apk.analyzer import APKCodeAnalyzer
        code = MagicMock()
        code.get_registers_size.return_value = 3
        code.get_bc.return_value = None
        encoded = _make_encoded_method(code=code)

        body, count, regs = APKCodeAnalyzer._disassemble_with_androguard(encoded)
        assert body is None
        assert count == 0
        assert regs == 3

    def test_basic_disassembly(self):
        from redb.extractors.decompiler.apk.analyzer import APKCodeAnalyzer

        instructions = []
        for name, output in [
            ("const/4", "v0, 0x0"),
            ("invoke-virtual", "v0, Lcom/Foo;->bar()V"),
            ("return-void", ""),
        ]:
            instr = MagicMock()
            instr.get_name.return_value = name
            instr.get_output.return_value = output
            instructions.append(instr)

        code = MagicMock()
        code.get_registers_size.return_value = 2
        bytecode = MagicMock()
        bytecode.get_instructions.return_value = instructions
        code.get_bc.return_value = bytecode

        encoded = _make_encoded_method(code=code)
        body, count, regs = APKCodeAnalyzer._disassemble_with_androguard(encoded)

        assert body is not None
        assert count == 3
        assert regs == 2
        assert "const/4" in body
        assert "return-void" in body

    def test_invoke_operands_normalized(self):
        """Androguard invoke output gets braces added around registers."""
        from redb.extractors.decompiler.apk.analyzer import APKCodeAnalyzer

        instr = MagicMock()
        instr.get_name.return_value = "invoke-virtual"
        # Androguard format: no braces
        instr.get_output.return_value = "v0, v1, Lcom/Foo;->bar(I)V"

        code = MagicMock()
        code.get_registers_size.return_value = 2
        bytecode = MagicMock()
        bytecode.get_instructions.return_value = [instr]
        code.get_bc.return_value = bytecode

        encoded = _make_encoded_method(code=code)
        body, count, regs = APKCodeAnalyzer._disassemble_with_androguard(encoded)

        # Should be normalized to apktool format with braces
        assert "{v0, v1}" in body

    def test_bytecode_exception_returns_none(self):
        from redb.extractors.decompiler.apk.analyzer import APKCodeAnalyzer

        code = MagicMock()
        code.get_registers_size.return_value = 2
        bytecode = MagicMock()
        bytecode.get_instructions.side_effect = RuntimeError("corrupt")
        code.get_bc.return_value = bytecode

        encoded = _make_encoded_method(code=code)
        body, count, regs = APKCodeAnalyzer._disassemble_with_androguard(encoded)

        assert body is None
        assert regs == 2