Xiaofeng Liu

170 papers A* 4A 3B 6C 9Misc 3Journal 96Unranked 48
YearRankTypeTitle / Venue / Authors
2026 J jnl
Comput. Mater. Continua
Zhao Li, Hongyu Xu, Shuai Zhang, Jintao Cui, Xiaofeng Liu
2026 J jnl
CoRR
Xiaofeng Liu, Jun Zhang, Fang-Wei Fu
2026 J jnl
Adv. Eng. Informatics
Xiaofeng Liu, Zheng Zhao, Daiping Wei, Fan Yang, Lin Bo, Jun Luo
2026 J jnl
CoRR
Saleh Afroogh, Seyd Ishtiaque Ahmed, Petra Ahrweiler, David Alvarez-Melis, Mansur Maturidi Arief, Emilia Barakova, Falco J. Bargagli-Stoffi, Erdem Biyik, Hanjie Chen, Xiang 'Anthony' Chen, Robert Clements, Keeley Crockett, Amit Dhurandhar, Fethiye Irmak Dogan, Mollie Dollinger, Motahhare Eslami, Aldo A. Faisal, Arya Farahi, Melanie Fernandez Pradie, Saadia Gabrie, Diego Garcia-Olano, Marzyeh Ghassemi, Shaona Ghosh, Hatice Gunes, Ehsan Hajiramezanali, Stefan Haufe, Biwei Huang, Angel Hwang, Md Tauhidul Islam, Junfeng Jiao, Amir-Hossein Karimi, Saber Kazeminasab, Anastasia Kuzminykh, William G. La Cava, Brian Y. Lim, Xiaofeng Liu, Mohammad R. K. Mofrad, Alicia Parrish, María Pérez-Ortiz, Shriti Raj, Swabha Swayamdipta, Salmon Talebi, Kush R. Varshney, Mihaela Vorvoreanu, Lily Weng, Alice Xiang, Yiming Xu, Ding Zhao, Jieyu Zhao
2026 A* conf
AAAI
Shike Li, Xiaokai Wang, Xiaofeng Liu, Xin Tong, Hu Zhang
2026 J jnl
Eng. Appl. Artif. Intell.
Yangbin Chen, Chenyang Xu, Chunfeng Liang, Yanbao Tao, Fan Du, Xiaofeng Liu, Mingqia Wang, Zhuoran Li, Jianyin Qiu, Chuan Shi
2026 J jnl
BMC Medical Informatics Decis. Mak.
Weiyi Wang, Libo Tong, Shiyang Li, Wei He, Jinyuan Huang, Xiaofeng Liu, Cui Wang, Junqi Xia, Xingshun Qi, Caiping Song
2026 J jnl
IEEE Trans. Netw. Serv. Manag.
Xiaofeng Liu, Naigong Zheng, Fuliang Li
2026 A* conf
AAAI
Rongfan Liao, Xiangyu Kong, Shiqing Tang, Lang He, Changzeng Fu, Weicheng Xie, Xiaofeng Liu, Lu Liu, Siyang Song
2026 J jnl
Biomed. Signal Process. Control.
Jiayu Lu, Jiaxin Li, Xiaofeng Liu, Dandan Li, Bin Wang, Miaomiao Liu
2026 J jnl
CoRR
Xingsheng Chen, Regina Zhang, Bo Gao, Xingwei He, Xiaofeng Liu, Pietro Lio, Kwok-Yan Lam, Siu-Ming Yiu
2026 J jnl
Inf. Sci.
Wenzheng Ma, Xiaofeng Liu, Yihu Liu, Yinglong Ma
2026 J jnl
CoRR
Ziyao Zeng, Chen Liu, Tianyu Liu, Hao Wang, Xiatao Sun, Fengyu Yang, Xiaofeng Liu, Zhiwen Fan
2026 J jnl
IEEE J. Sel. Areas Commun.
Wanshi Chen, Lingjia Liu, Xiaofeng Liu, Erik G. Larsson, Mohamed El Jaafari, Tony Q. S. Quek
2025 J jnl
CoRR
Xiaofeng Liu, Jun Zhang, Fang-Wei Fu
2025 J jnl
CoRR
Tajamul Ashraf, Mohammed Mohsen Peerzada, Moloud Abdar, Yutong Xie, Yuyin Zhou, Xiaofeng Liu, Iqra Altaf Gillani, Janibul Bashir
2025 J jnl
Pattern Anal. Appl.
Pingping Liu, Pengfei Chen, Xiaofeng Liu, Qiuzhan Zhou
2025 A* conf
CVPR
Wenxin Su, Song Tang, Xiaofeng Liu, Xiaojing Yi, Mao Ye, Chunxiao Zu, Jiahao Li, Xiatian Zhu
2025 conf
ACL (1)
Chaoqun Cui, Liangbin Huang, Shijing Wang, Zhe Tong, Zhaolong Huang, Xiao Zeng, Xiaofeng Liu
2025 J jnl
CoRR
Chaoqun Cui, Liangbin Huang, Shijing Wang, Zhe Tong, Zhaolong Huang, Xiao Zeng, Xiaofeng Liu
2025 J jnl
CoRR
Xiaobo Xia, Xiaofeng Liu, Jiale Liu, Kuai Fang, Lu Lu, Samet Oymak, William S. Currie, Tongliang Liu
2025 A conf
IROS
Hongtao Li, Ziyuan Jiao, Xiaofeng Liu, Hangxin Liu, Zilong Zheng
2025 J jnl
CoRR
Hongtao Li, Ziyuan Jiao, Xiaofeng Liu, Hangxin Liu, Zilong Zheng
2025 J jnl
Appl. Intell.
Yinglong Ma, Xiaofeng Liu, Chenqi Guo, Beihong Jin, Huili Liu
2025 J jnl
IEEE J. Sel. Top. Appl. Earth Obs. Remote. Sens.
Pingping Liu, Xiaofeng Liu, Zetong Liu, Haodong Li, Qiuzhan Zhou
2025 J jnl
IEEE Trans. Netw. Serv. Manag.
Huili Liu, Yinglong Ma, Chenqi Guo, Xiaofeng Liu, Tingdong Wang
2025 J jnl
npj Digit. Medicine
Zhaohui Bai, Su Lin, Mingyu Sun, Shanshan Yuan, Mariana Barros Marcondes, Dapeng Ma, Qiang Zhu, Yiling Li, Yingli He, Cyriac Abby Philips, Xiaofeng Liu, Kanokwan Pinyopornpanish, Lichun Shao, Nahum Méndez-Sánchez, Metin Basaranoglu, Yunhai Wu, Yu Chen, Ling Yang, Andrea Mancuso, Frank Tacke, Bimin Li, Lei Liu, Fanpu Ji, Xingshun Qi
2025 J jnl
Comput. Mater. Continua
Xiaofeng Liu, Yubin Bao, Fangling Leng
2025 J jnl
CoRR
Xiaofeng Liu, Jun Zhang, Fang-Wei Fu
2025 conf
IWSDA
Xiaofeng Liu, Jun Zhang, Fang-Wei Fu
2025 J jnl
CoRR
Pujun Xue, Junyi Ge, Xiaotong Jiang, Siyang Song, Zijian Wu, Yupeng Huo, Weicheng Xie, Linlin Shen, Xiaoqin Zhou, Xiaofeng Liu, Min Gu
2025 J jnl
Peer Peer Netw. Appl.
Haoran Shi, Zehua Chen, Yongqiang Cheng, Xiaofeng Liu, Qianqian Wang
2025 A* conf
ICLR
Xu Han, Linghao Jin, Xiaofeng Liu, Paul Pu Liang
2025 J jnl
CoRR
Jialin Wu, Xiaofeng Liu
2025 J jnl
CoRR
Xiaofeng Liu, Yalan Song
2025 J jnl
CoRR
Xiao Wu, Ting-Zhu Huang, Liang-Jian Deng, Xiaobing Yu, Yu Zhong, Shangqi Deng, Ufaq Khan, Jianghao Wu, Xiaofeng Liu, Imran Razzak, Xiaojun Chang, Yutong Xie
2025 J jnl
Comput. Electron. Agric.
Chengsai Fan, Xiaofeng Liu, Junyang Shi, Yinyan Shi, Ruiyin He
2025 J jnl
CoRR
Yingcong Li, Xiangyu Chang, Muti Kara, Xiaofeng Liu, Amit K. Roy-Chowdhury, Samet Oymak
2025 J jnl
Expert Syst. Appl.
Chenqi Guo, Shiwei Zhong, Xiaofeng Liu, Qianli Feng, Yinglong Ma
2024 J jnl
J. Comput. Inf. Sci. Eng.
Vinh Nguyen, Xiaofeng Liu, Jeremy Marvel
2024
Xiaofeng Liu
2024 J jnl
Sci. China Inf. Sci.
Wendong Zheng, Huaping Liu, Xiaofeng Liu, Fuchun Sun
2024 J jnl
CoRR
Wenxin Su, Song Tang, Xiaofeng Liu, Xiaojing Yi, Mao Ye, Chunxiao Zu, Jiahao Li, Xiatian Zhu
2024 J jnl
IEEE Trans. Netw. Sci. Eng.
Xiaofeng Liu, Chenqi Guo, Mingjun Zhao, Yinglong Ma
2024 J jnl
Database J. Biol. Databases Curation
Chong Peng, Xiaofeng Liu, Xiangbo Meng, Congge Chen, Xinming Wu, Lin Bai, Fuping Lu, Fufeng Liu
2024 J jnl
Neural Comput. Appl.
Yingjie Du, Xiaofeng Liu, Yuwei Yi, Kun Wei
2024 J jnl
Integr.
Han Bao, Ruoyu Ding, Xiaofeng Liu, Quan Xu
2024 conf
ICAIT
Bin Xiao, Quandong Huang, Xiaofeng Liu, Jiali Zhang, Ou Xu, Di Peng, Ya Han, Yuwen Qin
2024 J jnl
CoRR
Xu Han, Linghao Jin, Xiaofeng Liu, Paul Pu Liang
2024 J jnl
CoRR
Jiangming Shi, Xiangbo Yin, Yaoxing Wang, Xiaofeng Liu, Yuan Xie, Yanyun Qu
2024 J jnl
J. Frankl. Inst.
Xuyang Du, Guobao Liu, He Zhang, Ju H. Park, Xiaofeng Liu
2024 C conf
CSCWD
Xiaofeng Liu, Haibin Zhu, Dongning Liu
2024 conf
i-CREATe
Xiaofeng Liu, Hongmei Cui, Junhang Ding, Wenhao Wang, Yongli Wei
2024 J jnl
IEEE Trans. Fuzzy Syst.
Xiaofeng Liu, Yinglong Ma, Degang Chen, Ling Liu
2024 J jnl
CoRR
Chenqi Guo, Shiwei Zhong, Xiaofeng Liu, Qianli Feng, Yinglong Ma
2024 J jnl
Signal Process. Image Commun.
Zuopeng Zhao, Tianci Zheng, Kai Hao, Junjie Xu, Shuya Cui, Xiaofeng Liu, Guangming Zhao, Jie Zhou, Chen He
2023 J jnl
IEEE Syst. J.
Xiaofeng Liu, Difei Tang, Qinman He
2023 J jnl
Remote. Sens.
Pingping Liu, Xiaofeng Liu, Yifan Wang, Zetong Liu, Qiuzhan Zhou, Qingliang Li
2023 J jnl
CoRR
Chaopeng Shen, Alison P. Appling, Pierre Gentine, Toshiyuki Bandai, Hoshin V. Gupta, Alexandre Tartakovsky, Marco Baity-Jesi, Fabrizio Fenicia, Daniel Kifer, Li Li, Xiaofeng Liu, Wei Ren, Yi Zheng, Ciaran J. Harman, Martyn P. Clark, Matthew W. Farthing, Dapeng Feng, Praveen Kumar, Doaa Aboelyazeed, Farshid Rahmani, Hylke E. Beck, Tadd Bindas, Dipankar Dwivedi, Kuai Fang, Marvin Höge, Chris Rackauckas, Tirthankar Roy, Chonggang Xu, Kathryn Lawson
2023 J jnl
Sensors
Xiaofeng Liu, Zenglin Hong, Wei Shi, Xiaodan Guo
2023 J jnl
Image Vis. Comput.
Zuopeng Zhao, Kai Hao, Xiaofeng Liu, Tianci Zheng, Junjie Xu, Shuya Cui, Chen He, Jie Zhou, Guangming Zhao
2023 J jnl
CoRR
Jiaqi Xu, Cheng Luo, Weicheng Xie, Linlin Shen, Xiaofeng Liu, Lu Liu, Hatice Gunes, Siyang Song
2023 J jnl
Sensors
Yingjie Du, Xiaofeng Liu, Yuwei Yi, Kun Wei
2023 J jnl
BMC Bioinform.
Binyou Wang, Jianmin Guo, Xiaofeng Liu, Yang Yu, Jianming Wu, Yiwei Wang
2023 conf
CVPR Workshops
Jinjing Li, Qirong Liang, Qipei Li, Ruipeng Gang, Ji Fang, Chi-Chen Lin, Shuang Feng, Xiaofeng Liu
2023 conf
ICBAR
Xiaofeng Liu, Junyao Luo, Jianguang Ma
2023 J jnl
EURASIP J. Audio Speech Music. Process.
Lekai Zhang, Yingfan Wang, Kailun He, Hailong Zhang, Baixi Xing, Xiaofeng Liu, Fo Hu
2022 J jnl
Entropy
Zeliang Ou, Xiaofeng Liu, Hongwen Yang
2022 B conf
IWCMC
Xiaofeng Liu, Benjamin St. Amour, Arunita Jaekel
2022 Misc conf
ICISS
Ting Xie, Xiaofeng Liu
2022 J jnl
CoRR
Xiaofeng Liu, Yalan Song, Chaopeng Shen
2022 conf
CAIBDA
Xiaofeng Liu
2022 J jnl
Complex.
Xiaofeng Liu, Xiaoling Wu, Yumin Qiu, Shuping Cheng
2022 J jnl
Softw. Impacts
Xiaofeng Liu, Huili Liu, Yinglong Ma
2022 J jnl
Expert Syst. Appl.
Yinglong Ma, Xiaofeng Liu, Lijiao Zhao, Yue Liang, Peng Zhang, Beihong Jin
2022 J jnl
Symmetry
Jiafu Zhuang, Xiaofeng Liu, Wei Zhuang
2022 J jnl
Decis. Sci.
Jue Liu, Xiaofeng Liu, Houcai Shen
2021 J jnl
Complex.
Xiaofeng Liu, Zubair Ahmad, Saima K. Khosa, Mohammed Yusuf, Osama Abdulaziz Alamri, Walid Emam
2021 J jnl
Int. J. Intell. Syst.
Xiangwei Zheng, Xiaofeng Liu, Yuang Zhang, Lizhen Cui, Xiaomei Yu
2021 J jnl
J. Intell. Syst.
Xiaofeng Liu, Pradeep Kumar Singh, Anton Pavlovich Pljonkin
2021 C conf
BROADNETS
Yinghao Gan, Haoran Cai, Xiaofeng Liu, Yanmin Wang
2021 J jnl
IEEE Wirel. Commun. Lett.
Wendong Liu, Wenqiang Tian, Han Xiao, Shi Jin, Xiaofeng Liu, Jia Shen
2021 J jnl
CoRR
Rodrigo Cabrera, Xiaofeng Liu, Mohammadreza Ghodsi, Zebulun Matteson, Eugene Weinstein, Anjuli Kannan
2021 A conf
IROS
Qihao Liu, Yujia Wang, Xiaofeng Liu
2021 J jnl
Comput. Intell. Neurosci.
Zuopeng Zhao, Kai Hao, Xiaoping Ma, Xiaofeng Liu, Tianci Zheng, Junjie Xu, Shuya Cui
2021 J jnl
Entropy
Bing Sun, Xiaofeng Liu
2021 C conf
BROADNETS
Xiaofeng Liu, Ning Li, Yuchen Liu, Yujia He
2021 J jnl
CoRR
Yalan Song, Chaopeng Shen, Xiaofeng Liu
2020 J jnl
Neural Process. Lett.
Xiaolei Jiang, Erchong Liao, Xiaofeng Liu
2020 J jnl
CoRR
Xiaofeng Liu
2020 conf
MLICOM
Yingzhe Dou, Xiaofeng Liu, Mingchuan Yang
2020 J jnl
IEEE Access
Jianshu Qiao, Xiaofeng Liu, Zehua Chen
2020 Misc conf
ICASSP
Mohammadreza Ghodsi, Xiaofeng Liu, James Apfel, Rodrigo Cabrera, Eugene Weinstein
2020 J jnl
Int. J. Inf. Commun. Technol.
Xiaofeng Liu
2019 conf
CSPS
Xiaoye Jing, Xiaofeng Liu, Min Jia, Qing Guo, Xuemai Gu
2019 conf
WISATS (1)
Jintian Sun, Xiaofeng Liu, Guanghui Ren, Min Jia, Qing Guo
2019 J jnl
IEEE Access
Qiaoqin Li, Yongguo Liu, Jiajing Zhu, Yonghua Xiao, Hao Wu, Xiaofeng Liu, Zijie Chen, Shuangqing Zhai
2019 conf
SSIP
Xiaofeng Liu, Hui Zou, Wanyu Niu, Yuqing Song, Wenzhang He
2019 J jnl
Sensors
Zhifeng Cheng, Lu Sun, Fuhe Liu, Xiaofeng Liu, Lei Li, Quanchao Li, Richa Hu
2019 conf
CSPS
Tahir Arshad, Min Jia, Qing Guo, Xuemai Gu, Xiaofeng Liu
2019 J jnl
Neurocomputing
Jiajing Zhu, Yongguo Liu, Yun Zhang, Zhi Chen, Qiaoqin Li, Shangming Yang, Xiaofeng Liu, Shuangqing Zhai, Yi Zhang, Chuanbiao Wen
2019 J jnl
Sensors
Fuhe Liu, Zhifeng Cheng, Ping Jia, Bao Zhang, Xiaofeng Liu, Rizha Hu
2019 A conf
CIKM
Yun Zhang, Yongguo Liu, Jiajing Zhu, Ziqiang Zheng, Xiaofeng Liu, Weiguang Wang, Zijie Chen, Shuangqing Zhai
2019 J jnl
IEEE Trans. Instrum. Meas.
Chunxia Yue, Hui Ding, Xiaofeng Liu
2019 B conf
DCOSS
Oluwaseyi Akinlade, Ikjot Saini, Xiaofeng Liu, Arunita Jaekel
2019 J jnl
计算机科学
Xu Wang, Zhigui Lin, Xiaofeng Liu, Dejun Meng
2018 conf
DIVANet
Maan Joseph, Xiaofeng Liu, Arunita Jaekel
2018 J jnl
IEEE Syst. J.
Xiaofeng Liu, Bingtuan Gao, Cheng Wu, Yi Tang
2018 conf
CSAI/ICIMT
Xiaofeng Liu
2018 J jnl
Int. J. Data Min. Bioinform.
Xiaofeng Liu, Bin Hu, Xiangwei Zheng, Xiaowei Li
2018 J jnl
J. Commun. Inf. Networks
Min Jia, Ximu Zhang, Xuemai Gu, Xiaofeng Liu, Qing Guo
2018 conf
ISWCS
Lulu Wang, Yuehong Gao, Yangming Dong, Zhidu Li, Zhening Zhang, Xin Zhang, Xiaofeng Liu
2017 C conf
ISCAS
Yudong Zhang, Xiaofeng Liu, Woogeun Rhee, Hanjun Jiang, Zhihua Wang
2017 conf
CSPS
Ximu Zhang, Xiaofeng Liu, Haitao Wang, Zhihui Liu, Min Jia
2017 conf
MLICOM (1)
Yumeng Zhang, Mingchuan Yang, Xiaofeng Liu
2017 conf
CSPS
Yumeng Zhang, Xiaofeng Liu, Mingchuan Yang
2017 conf
GSKI (2)
Xiaofeng Liu
2017 J jnl
Entropy
Yi Tang, Jing Ling, Cheng Wu, Ning Chen, Xiaofeng Liu, Bingtuan Gao
2017 conf
CSPS
Shujing Zhang, Mingchuan Yang, Xinye Shao, Xiaofeng Liu
2017 Misc conf
ICASSP
Aren Jansen, Jort F. Gemmeke, Daniel P. W. Ellis, Xiaofeng Liu, Wade Lawrence, Dylan Freedman
2016 conf
DSC
Xiaofeng Liu, Qixu Liu, Xiaoxi Wang, Zhaopeng Jia
2016 J jnl
Comput. Stand. Interfaces
Demin Gao, Haifeng Lin, Xiaofeng Liu
2016 J jnl
IEEE Trans. Ind. Electron.
Yong Liu, Rong Xiong, Yue Wang, Hong Huang, Xiaojia Xie, Xiaofeng Liu, Gaoming Zhang
2016 J jnl
Kybernetes
Canzhong Yao, Xiaofeng Liu, Ji-Nan Lin
2015 conf
A-SSCC
Dang Liu, Xiaofeng Liu, Woogeun Rhee, Zhihua Wang
2015 conf
VTC Spring
Lei Cheng, Yuehong Gao, Yuancao Li, Dacheng Yang, Xiaofeng Liu
2015 conf
ICIA
Xiaofeng Liu, Haitao Zhang, Sen Zhang
2015 J jnl
CoRR
Krzysztof Choromanski, Sanjiv Kumar, Xiaofeng Liu
2014 J jnl
Bioinform.
Konrad Krawczyk, Xiaofeng Liu, Terry Baker, Jiye Shi, Charlotte M. Deane
2014 conf
VTC Fall
Yuancao Li, Zhesheng Lin, Huan Liu, Yuehong Gao, Xin Zhang, Xiaofeng Liu
2013 C conf
ICCC
Xiaofeng Liu
2013 conf
MCV
Zhennan Yan, Shaoting Zhang, Xiaofeng Liu, Dimitris N. Metaxas, Albert Montillo
2013 conf
ISBI
Zhennan Yan, Shaoting Zhang, Xiaofeng Liu, Dimitris N. Metaxas, Albert Montillo
2013 C conf
NPC
Xiaofeng Liu, Youjian Zhao, Yajuan Wu
2013 conf
MICCAI (1)
Xiaofeng Liu, Albert Montillo, Ek T. Tan, John F. Schenck, Paulo R. S. Mendonça
2013 J jnl
Eng. Appl. Artif. Intell.
Xueying Zhang, Xiaofeng Liu, Zizhong John Wang
2013 conf
GreenCom/iThings/CPScom
Yixiong Wang, Jun Song, Xiaofeng Liu, Shan Jiang, Yunfei Liu
2013 B conf
Image Processing
Xiaofeng Liu, Albert Montillo, Ek T. Tan, John F. Schenck
2012 conf
IFTC
Fuqiang Liu, Xiaofeng Liu, Pingting Luo, Yingqian Yang, Danqing Shi
2012 conf
VTC Spring
Run-nan Cai, Ming-Chuan Yang, Xingqi Zhang, Ming Li, Xiaofeng Liu
2012 conf
ISBI
Xiaofeng Liu, Satish G. Iyengar, Jens Rittscher
2012 conf
STIA
Xiaofeng Liu, Dirk Ryan Padfield
2012 conf
VTC Fall
Qiuping Huang, Xiaofeng Liu, Hongwen Yang
2012 C conf
ICCC
Gu Lv, Mingchuan Yang, Hui Li, Ming Li, Xiaofeng Liu
2012 conf
ICVES
Yingqian Yang, Fuqiang Liu, Ping Wang, Pingting Luo, Xiaofeng Liu
2011 J jnl
J. Networks
Xingwei Wang, Huanyan Zhao, Min Huang, Zhankao Wen, Weixin Wu, Xiaofeng Liu
2011 conf
ISBI
Xiaofeng Liu, Marius George Linguraru, Jianhua Yao, Ronald M. Summers
2011 J jnl
Int. J. Granul. Comput. Rough Sets Intell. Syst.
Quanquan Gao, Xiaofeng Liu, Tingge Ren, Yan Sun, Fan Zhang, Yongyi Chen
2011 J jnl
Ad Hoc Sens. Wirel. Networks
Xiaofeng Liu, Liusheng Huang, Hongli Xu, Wenbo Shi, Dashan Wang
2011 J jnl
J. Comput. Phys.
Xiaofeng Liu
2011 conf
HISB
Xiaofeng Liu, Qi Song, Paulo R. S. Mendonça, Xiaodong Tao, Rahul Bhotika
2011 conf
EMEIT
Xinhui Liu, Wei Wang, Xiaofeng Liu
2011 conf
ICIC (3)
Hongwei Gao, Xiaofeng Liu, Jinguo Liu, Fuguo Chen, Ben Niu
2011 B conf
PIMRC
Qiuping Huang, Xiaofeng Liu, Hongwen Yang
2010 B conf
ICPADS
Xiaofeng Liu, Xiaofei Xu, Shengchun Deng
2010 conf
ICSS
Zhongjie Wang, Xiaofei Xu, Dian-Hui Chu, Lanshun Nie, Tianyi Zang, Xiaofeng Liu
2010 conf
BIC-TA
Bo Wu, Shiyue Wu, Xiaofeng Liu
2010 conf
MIAR
Xiaofeng Liu, Marius George Linguraru, Jianhua Yao, Ronald M. Summers
2010 conf
FSKD
Xiaofeng Liu, Daniel Sun, Yuntao Chang, Zhongren Peng
2009 conf
IESA
Xiaofeng Liu, Xiaofei Xu, Shengchun Deng
2008 conf
WSAN
Xiaofeng Liu, Liusheng Huang, Wenbo Shi, Hongli Xu
2008 J jnl
IEEE Commun. Lett.
Xiaofeng Liu, Hongwen Yang
2008 conf
ISCSCT (2)
Yajuan Wu, Minghui Wang, Xiaofeng Liu, Yi Zhan
2007 conf
SKG
Xiaofeng Liu, Chuanbo Chen, Yunsheng Liu
2007 conf
SNPD (1)
Xiaofeng Liu, Chuanbo Chen, Yunsheng Liu
2006 C conf
ATC
Yingyuan Xiao, Yunsheng Liu, Xiangyang Chen, Xiaofeng Liu
2006 J jnl
IEICE Trans. Commun.
Xiaofeng Liu, Hongwen Yang, Wenbin Guo, Dacheng Yang
2006 J jnl
Comput. Biol. Medicine
Xiaofeng Liu, Huan Qi, Supin Wang, Mingxi Wan
2005 C conf
ISPA
Yingyuan Xiao, Yunsheng Liu, Guoqiong Liao, Xiaofeng Liu
2003 B conf
Image Processing
Xiaofeng Liu, Hakan Cevikalp, J. Michael Fitzpatrick
tests/unit/test_apk_code_analyzer.py
← Index tests/unit/test_apk_code_analyzer.py python
"""
Unit tests for APK code analysis components.

Tests APKCodeAnalyzer, LibraryFilter, SmaliParser, method enumeration,
library filtering, and method key matching. All external tools are mocked.
"""
import os
import tempfile
import textwrap

import pytest
from unittest.mock import MagicMock, patch, PropertyMock

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


def _has_mmh3():
    try:
        import mmh3  # noqa: F401
        return True
    except ImportError:
        return False


# ============================================================================
# LibraryFilter Tests
# ============================================================================

class TestLibraryFilter:
    """Tests for package-based library filtering."""

    def test_default_prefixes_loaded(self):
        from redb.extractors.decompiler.apk.library_filter import LibraryFilter
        lf = LibraryFilter()
        assert lf.is_library("android.app.Activity")
        assert lf.is_library("androidx.core.app.NotificationCompat")

    def test_user_class_not_filtered(self):
        from redb.extractors.decompiler.apk.library_filter import LibraryFilter
        lf = LibraryFilter()
        assert not lf.is_library("com.example.myapp.MainActivity")
        assert not lf.is_library("org.myorg.MyClass")

    def test_dalvik_descriptor_format(self):
        from redb.extractors.decompiler.apk.library_filter import LibraryFilter
        lf = LibraryFilter()
        assert lf.is_library("Landroid/app/Activity;")
        assert lf.is_library("Landroidx/core/app/NotificationCompat;")
        assert not lf.is_library("Lcom/example/myapp/MainActivity;")

    def test_custom_prefixes(self):
        from redb.extractors.decompiler.apk.library_filter import LibraryFilter
        lf = LibraryFilter(prefixes=["com.custom."])
        assert lf.is_library("com.custom.SomeClass")
        assert not lf.is_library("android.app.Activity")

    def test_env_var_override(self):
        from redb.extractors.decompiler.apk.library_filter import LibraryFilter
        with patch.dict(os.environ, {"APK_LIBRARY_PREFIXES": "my.lib.,other.lib."}):
            lf = LibraryFilter()
            assert lf.is_library("my.lib.SomeClass")
            assert lf.is_library("other.lib.AnotherClass")
            assert not lf.is_library("android.app.Activity")

    def test_filter_stats(self):
        from redb.extractors.decompiler.apk.library_filter import LibraryFilter
        lf = LibraryFilter()
        lf.is_library("android.app.Activity")
        lf.is_library("com.example.MyClass")
        lf.is_library("kotlin.Unit")
        stats = lf.get_filter_stats()
        assert stats["library"] == 2
        assert stats["user"] == 1

    def test_normalize_class_name(self):
        from redb.extractors.decompiler.apk.library_filter import LibraryFilter
        assert LibraryFilter._normalize_class_name("Lcom/example/Foo;") == "com.example.Foo"
        assert LibraryFilter._normalize_class_name("com.example.Foo") == "com.example.Foo"
        assert LibraryFilter._normalize_class_name("com/example/Foo") == "com.example.Foo"

    def test_google_libraries_filtered(self):
        from redb.extractors.decompiler.apk.library_filter import LibraryFilter
        lf = LibraryFilter()
        assert lf.is_library("com.google.android.gms.ads.AdView")
        assert lf.is_library("com.google.firebase.messaging.FirebaseMessagingService")
        assert lf.is_library("com.google.gson.Gson")

    def test_kotlin_filtered(self):
        from redb.extractors.decompiler.apk.library_filter import LibraryFilter
        lf = LibraryFilter()
        assert lf.is_library("kotlin.Unit")
        assert lf.is_library("kotlinx.coroutines.CoroutineScope")

    def test_third_party_libs_filtered(self):
        from redb.extractors.decompiler.apk.library_filter import LibraryFilter
        lf = LibraryFilter()
        assert lf.is_library("com.squareup.okhttp3.OkHttpClient")
        assert lf.is_library("io.reactivex.Observable")
        assert lf.is_library("org.apache.commons.io.IOUtils")

    def test_empty_class_name(self):
        from redb.extractors.decompiler.apk.library_filter import LibraryFilter
        lf = LibraryFilter()
        assert not lf.is_library("")


# ============================================================================
# SmaliParser Tests
# ============================================================================

class TestSmaliParser:
    """Tests for smali file parsing."""

    SAMPLE_SMALI = textwrap.dedent("""\
        .class public Lcom/example/MyClass;
        .super Ljava/lang/Object;

        .method public constructor <init>()V
            .registers 1
            invoke-direct {p0}, Ljava/lang/Object;-><init>()V
            return-void
        .end method

        .method public onCreate(Landroid/os/Bundle;)V
            .registers 4
            .param p1, "savedInstanceState"

            .line 10
            invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V
            const/high16 v0, 0x7f090000
            invoke-virtual {p0, v0}, Lcom/example/MyClass;->setContentView(I)V
            const-string v1, "hello"
            invoke-virtual {p0, v1}, Lcom/example/MyClass;->log(Ljava/lang/String;)V
            return-void
        .end method

        .method public abstract doSomething()V
        .end method

        .method public native nativeMethod()V
        .end method
    """)

    def test_parse_smali_content(self):
        from redb.extractors.decompiler.apk.smali_parser import SmaliParser
        methods = SmaliParser._parse_smali_content(self.SAMPLE_SMALI)
        # abstract and native should be skipped
        assert len(methods) == 2

    def test_method_names_extracted(self):
        from redb.extractors.decompiler.apk.smali_parser import SmaliParser
        methods = SmaliParser._parse_smali_content(self.SAMPLE_SMALI)
        names = {m.method_name for m in methods}
        assert "<init>" in names
        assert "onCreate" in names

    def test_class_name_extracted(self):
        from redb.extractors.decompiler.apk.smali_parser import SmaliParser
        methods = SmaliParser._parse_smali_content(self.SAMPLE_SMALI)
        for m in methods:
            assert m.class_name == "Lcom/example/MyClass;"

    def test_method_signature(self):
        from redb.extractors.decompiler.apk.smali_parser import SmaliParser
        methods = SmaliParser._parse_smali_content(self.SAMPLE_SMALI)
        oncreate = [m for m in methods if m.method_name == "onCreate"][0]
        assert oncreate.method_signature == "(Landroid/os/Bundle;)V"

    def test_instruction_count(self):
        from redb.extractors.decompiler.apk.smali_parser import SmaliParser
        methods = SmaliParser._parse_smali_content(self.SAMPLE_SMALI)
        oncreate = [m for m in methods if m.method_name == "onCreate"][0]
        # invoke-super, const/high16, invoke-virtual, const-string, invoke-virtual, return-void
        assert oncreate.instruction_count == 6

    def test_register_count(self):
        from redb.extractors.decompiler.apk.smali_parser import SmaliParser
        methods = SmaliParser._parse_smali_content(self.SAMPLE_SMALI)
        oncreate = [m for m in methods if m.method_name == "onCreate"][0]
        assert oncreate.register_count == 4

    def test_register_count_locals_directive(self):
        """apktool outputs .locals by default, not .registers."""
        from redb.extractors.decompiler.apk.smali_parser import SmaliParser
        smali_with_locals = textwrap.dedent("""\
            .class public Lcom/example/Locals;
            .super Ljava/lang/Object;

            .method public doWork()V
                .locals 3
                const/4 v0, 0x0
                const/4 v1, 0x1
                add-int v2, v0, v1
                return-void
            .end method
        """)
        methods = SmaliParser._parse_smali_content(smali_with_locals)
        assert len(methods) == 1
        assert methods[0].register_count == 3

    def test_abstract_native_skipped(self):
        from redb.extractors.decompiler.apk.smali_parser import SmaliParser
        methods = SmaliParser._parse_smali_content(self.SAMPLE_SMALI)
        names = {m.method_name for m in methods}
        assert "doSomething" not in names
        assert "nativeMethod" not in names

    def test_normalize_smali_body(self):
        from redb.extractors.decompiler.apk.smali_parser import SmaliParser
        body = textwrap.dedent("""\
            .registers 4
            .line 10
            # comment
            invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V

            const-string v1, "hello"
        """)
        normalized = SmaliParser.normalize_smali_body(body)
        assert ".line" not in normalized
        assert "# comment" not in normalized
        assert "invoke-super" in normalized
        assert "const-string" in normalized

    def test_count_instructions(self):
        from redb.extractors.decompiler.apk.smali_parser import SmaliParser
        body = textwrap.dedent("""\
            .registers 4
            .param p1, "x"
            invoke-virtual {p0}, Lcom/example/Foo;->bar()V
            const/4 v0, 0x0
            :label_0
            if-eqz v0, :label_1
            return-void
            :label_1
            goto :label_0
        """)
        assert SmaliParser.count_instructions(body) == 5

    def test_make_method_key(self):
        from redb.extractors.decompiler.apk.smali_parser import SmaliParser
        key = SmaliParser.make_method_key(
            "Lcom/example/Foo;", "bar", "(I)V"
        )
        assert key == "Lcom/example/Foo;->bar(I)V"

    def test_parse_smali_file(self):
        from redb.extractors.decompiler.apk.smali_parser import SmaliParser
        with tempfile.NamedTemporaryFile(
            mode="w", suffix=".smali", delete=False
        ) as f:
            f.write(self.SAMPLE_SMALI)
            f.flush()
            try:
                methods = SmaliParser.parse_smali_file(f.name)
                assert len(methods) == 2
            finally:
                os.unlink(f.name)

    def test_parse_smali_directory(self):
        from redb.extractors.decompiler.apk.smali_parser import SmaliParser
        with tempfile.TemporaryDirectory() as tmpdir:
            # Create nested dir structure
            pkg_dir = os.path.join(tmpdir, "com", "example")
            os.makedirs(pkg_dir)
            with open(os.path.join(pkg_dir, "MyClass.smali"), "w") as f:
                f.write(self.SAMPLE_SMALI)

            result = SmaliParser.parse_smali_directory(tmpdir)
            assert len(result) == 2
            # Check keys are in expected format
            for key in result:
                assert "->" in key

    def test_empty_smali_file(self):
        from redb.extractors.decompiler.apk.smali_parser import SmaliParser
        methods = SmaliParser._parse_smali_content("")
        assert methods == []

    def test_class_only_no_methods(self):
        from redb.extractors.decompiler.apk.smali_parser import SmaliParser
        content = ".class public Lcom/example/Empty;\n.super Ljava/lang/Object;\n"
        methods = SmaliParser._parse_smali_content(content)
        assert methods == []


# ============================================================================
# MethodExtractor Tests (Hashing, Obfuscation, Type Conversion)
# ============================================================================

class TestMethodExtractor:
    """Tests for method-level content extraction and hashing."""

    def test_compute_sha256(self):
        from redb.extractors.decompiler.apk.method_extractor import compute_sha256
        h = compute_sha256("test content")
        assert len(h) == 64
        assert h == compute_sha256("test content")  # deterministic

    def test_compute_sha256_different_input(self):
        from redb.extractors.decompiler.apk.method_extractor import compute_sha256
        assert compute_sha256("a") != compute_sha256("b")

    def test_compute_ssdeep_short_input(self):
        from redb.extractors.decompiler.apk.method_extractor import compute_ssdeep
        result = compute_ssdeep("short")
        assert result is None

    def test_compute_ssdeep_long_input(self):
        from redb.extractors.decompiler.apk.method_extractor import compute_ssdeep
        result = compute_ssdeep("x" * 200)
        # May be None depending on entropy, but shouldn't crash
        assert result is None or isinstance(result, str)

    def test_compute_tlsh_short_input(self):
        from redb.extractors.decompiler.apk.method_extractor import compute_tlsh
        result = compute_tlsh("short")
        assert result is None

    def test_compute_tlsh_long_input(self):
        from redb.extractors.decompiler.apk.method_extractor import compute_tlsh
        data = "".join(chr(i % 256) for i in range(200))
        result = compute_tlsh(data)
        assert result is None or isinstance(result, str)

    def test_compute_minhash_returns_none_without_mmh3(self):
        from unittest.mock import patch
        from redb.extractors.decompiler.apk.method_extractor import compute_minhash
        with patch.dict("sys.modules", {"mmh3": None}):
            # Force reimport to hit ImportError
            import importlib
            import redb.extractors.decompiler.apk.method_extractor as mod
            importlib.reload(mod)
            result = mod.compute_minhash("line1\nline2\nline3\nline4")
            # mmh3 may or may not be available; just check it doesn't crash
            assert result is None or isinstance(result, list)
            importlib.reload(mod)  # restore

    def test_compute_minhash_too_few_lines(self):
        from redb.extractors.decompiler.apk.method_extractor import compute_minhash
        result = compute_minhash("invoke-direct {v0}, Lfoo;->bar()V")
        # Only 1 instruction line, need at least 3 for 3-grams
        assert result is None

    @pytest.mark.skipif(
        not _has_mmh3(), reason="mmh3 not installed"
    )
    def test_compute_minhash_deterministic(self):
        from redb.extractors.decompiler.apk.method_extractor import compute_minhash
        smali = (
            "invoke-direct {v1}, Ljava/lang/Object;-><init>()V\n"
            "const-string v0, \"hello\"\n"
            "iput-object v0, v1, LA;->a:Ljava/lang/String;\n"
            "return-void"
        )
        sig1 = compute_minhash(smali)
        sig2 = compute_minhash(smali)
        assert sig1 is not None
        assert sig1 == sig2

    @pytest.mark.skipif(
        not _has_mmh3(), reason="mmh3 not installed"
    )
    def test_compute_minhash_signature_length(self):
        from redb.extractors.decompiler.apk.method_extractor import compute_minhash
        smali = (
            "invoke-direct {v1}, Ljava/lang/Object;-><init>()V\n"
            "const-string v0, \"hello\"\n"
            "iput-object v0, v1, LA;->a:Ljava/lang/String;\n"
            "return-void"
        )
        sig = compute_minhash(smali)
        assert sig is not None
        assert len(sig) == 64  # SIGNATURE_LENGTH

    @pytest.mark.skipif(
        not _has_mmh3(), reason="mmh3 not installed"
    )
    def test_compute_minhash_value_range(self):
        from redb.extractors.decompiler.apk.method_extractor import compute_minhash
        smali = (
            "invoke-direct {v1}, Ljava/lang/Object;-><init>()V\n"
            "const-string v0, \"hello\"\n"
            "iput-object v0, v1, LA;->a:Ljava/lang/String;\n"
            "return-void"
        )
        sig = compute_minhash(smali)
        assert sig is not None
        for val in sig:
            assert 0 <= val < 256  # 8-bit values

    @pytest.mark.skipif(
        not _has_mmh3(), reason="mmh3 not installed"
    )
    def test_compute_minhash_different_inputs_differ(self):
        from redb.extractors.decompiler.apk.method_extractor import compute_minhash
        smali_a = (
            "invoke-direct {v1}, Ljava/lang/Object;-><init>()V\n"
            "const-string v0, \"hello\"\n"
            "iput-object v0, v1, LA;->a:Ljava/lang/String;\n"
            "return-void"
        )
        smali_b = (
            "sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;\n"
            "const-string v1, \"world\"\n"
            "invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V\n"
            "return-void"
        )
        sig_a = compute_minhash(smali_a)
        sig_b = compute_minhash(smali_b)
        assert sig_a is not None
        assert sig_b is not None
        assert sig_a != sig_b

    @pytest.mark.skipif(
        not _has_mmh3(), reason="mmh3 not installed"
    )
    def test_compute_minhash_skips_directives_and_labels(self):
        from redb.extractors.decompiler.apk.method_extractor import compute_minhash
        # Directives and labels should be filtered out
        smali = (
            ".registers 3\n"
            ":start\n"
            "invoke-direct {v1}, Ljava/lang/Object;-><init>()V\n"
            ".line 10\n"
            "const-string v0, \"hello\"\n"
            "iput-object v0, v1, LA;->a:Ljava/lang/String;\n"
            "return-void"
        )
        smali_clean = (
            "invoke-direct {v1}, Ljava/lang/Object;-><init>()V\n"
            "const-string v0, \"hello\"\n"
            "iput-object v0, v1, LA;->a:Ljava/lang/String;\n"
            "return-void"
        )
        sig_with_directives = compute_minhash(smali)
        sig_clean = compute_minhash(smali_clean)
        assert sig_with_directives == sig_clean

    def test_dalvik_to_java_class(self):
        from redb.extractors.decompiler.apk.method_extractor import dalvik_to_java_class
        assert dalvik_to_java_class("Lcom/example/Foo;") == "com.example.Foo"
        assert dalvik_to_java_class("com.example.Foo") == "com.example.Foo"

    def test_dalvik_type_to_java(self):
        from redb.extractors.decompiler.apk.method_extractor import dalvik_type_to_java
        assert dalvik_type_to_java("V") == "void"
        assert dalvik_type_to_java("I") == "int"
        assert dalvik_type_to_java("Z") == "boolean"
        assert dalvik_type_to_java("Ljava/lang/String;") == "String"
        assert dalvik_type_to_java("[I") == "int[]"
        assert dalvik_type_to_java("[Ljava/lang/String;") == "String[]"

    def test_dalvik_to_java_prototype(self):
        from redb.extractors.decompiler.apk.method_extractor import dalvik_to_java_prototype
        result = dalvik_to_java_prototype("onCreate", "(Landroid/os/Bundle;)V")
        assert result == "void onCreate(Bundle)"

    def test_dalvik_to_java_prototype_no_params(self):
        from redb.extractors.decompiler.apk.method_extractor import dalvik_to_java_prototype
        result = dalvik_to_java_prototype("toString", "()Ljava/lang/String;")
        assert result == "String toString()"

    def test_dalvik_to_java_prototype_multiple_params(self):
        from redb.extractors.decompiler.apk.method_extractor import dalvik_to_java_prototype
        result = dalvik_to_java_prototype("foo", "(ILjava/lang/String;Z)V")
        assert result == "void foo(int, String, boolean)"

    def test_detect_obfuscation_short_name(self):
        from redb.extractors.decompiler.apk.method_extractor import detect_obfuscation_indicators
        result = detect_obfuscation_indicators("a", "Lcom/example/Foo;", "", 10)
        assert result["short_method_name"] is True
        result = detect_obfuscation_indicators("onCreate", "Lcom/example/Foo;", "", 10)
        assert result["short_method_name"] is False

    def test_detect_obfuscation_short_class(self):
        from redb.extractors.decompiler.apk.method_extractor import detect_obfuscation_indicators
        result = detect_obfuscation_indicators("foo", "Lcom/example/a;", "", 10)
        assert result["short_class_name"] is True
        result = detect_obfuscation_indicators("foo", "Lcom/example/MainActivity;", "", 10)
        assert result["short_class_name"] is False

    def test_detect_reflection_calls(self):
        from redb.extractors.decompiler.apk.method_extractor import detect_obfuscation_indicators
        smali = "invoke-virtual {v0}, Ljava/lang/reflect/Method;->invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"
        result = detect_obfuscation_indicators("foo", "Lcom/Foo;", smali, 10)
        assert result["has_reflection_calls"] is True

    def test_detect_no_reflection(self):
        from redb.extractors.decompiler.apk.method_extractor import detect_obfuscation_indicators
        smali = "invoke-virtual {p0}, Lcom/example/Foo;->bar()V"
        result = detect_obfuscation_indicators("foo", "Lcom/Foo;", smali, 10)
        assert result["has_reflection_calls"] is False

    def test_detect_string_encryption(self):
        from redb.extractors.decompiler.apk.method_extractor import detect_obfuscation_indicators
        smali = 'const-string v0, "encrypted"\n    invoke-static {v0}, Lcom/example/Crypto;->decrypt(Ljava/lang/String;)Ljava/lang/String;'
        result = detect_obfuscation_indicators("foo", "Lcom/Foo;", smali, 10)
        assert result["has_string_encryption"] is True

    def test_detect_excessive_goto(self):
        from redb.extractors.decompiler.apk.method_extractor import detect_obfuscation_indicators
        smali = "\n".join(["goto :label_0"] * 20)
        result = detect_obfuscation_indicators("foo", "Lcom/Foo;", smali, 30)
        # threshold = max(5, 30*0.15) = 5, 20 > 5
        assert result["excessive_goto_count"] is True

    def test_detect_no_excessive_goto(self):
        from redb.extractors.decompiler.apk.method_extractor import detect_obfuscation_indicators
        smali = "goto :label_0\nreturn-void"
        result = detect_obfuscation_indicators("foo", "Lcom/Foo;", smali, 100)
        assert result["excessive_goto_count"] is False

    def test_parse_dalvik_params_empty(self):
        from redb.extractors.decompiler.apk.method_extractor import _parse_dalvik_params
        assert _parse_dalvik_params("") == []

    def test_parse_dalvik_params_primitives(self):
        from redb.extractors.decompiler.apk.method_extractor import _parse_dalvik_params
        result = _parse_dalvik_params("IZJ")
        assert result == ["I", "Z", "J"]

    def test_parse_dalvik_params_object(self):
        from redb.extractors.decompiler.apk.method_extractor import _parse_dalvik_params
        result = _parse_dalvik_params("Ljava/lang/String;")
        assert result == ["Ljava/lang/String;"]

    def test_parse_dalvik_params_mixed(self):
        from redb.extractors.decompiler.apk.method_extractor import _parse_dalvik_params
        result = _parse_dalvik_params("ILjava/lang/String;Z")
        assert result == ["I", "Ljava/lang/String;", "Z"]

    def test_parse_dalvik_params_array(self):
        from redb.extractors.decompiler.apk.method_extractor import _parse_dalvik_params
        result = _parse_dalvik_params("[I[Ljava/lang/String;")
        assert result == ["[I", "[Ljava/lang/String;"]


# ============================================================================
# APKCodeAnalyzer Tests (mocked tools)
# ============================================================================

class TestAPKCodeAnalyzer:
    """Tests for the APKCodeAnalyzer orchestration."""

    def test_init_defaults(self):
        from redb.extractors.decompiler.apk.analyzer import APKCodeAnalyzer
        analyzer = APKCodeAnalyzer("/fake/path.apk")
        assert analyzer.filepath == "/fake/path.apk"
        assert analyzer.timeout == 600
        assert analyzer.min_instructions == 5

    def test_init_custom_timeout(self):
        from redb.extractors.decompiler.apk.analyzer import APKCodeAnalyzer
        analyzer = APKCodeAnalyzer("/fake/path.apk", timeout=300)
        assert analyzer.timeout == 300

    def test_init_min_instructions_env(self):
        from redb.extractors.decompiler.apk.analyzer import APKCodeAnalyzer
        with patch.dict(os.environ, {"APK_MIN_METHOD_INSTRUCTIONS": "10"}):
            analyzer = APKCodeAnalyzer("/fake/path.apk")
            assert analyzer.min_instructions == 10

    @patch("redb.extractors.decompiler.apk.analyzer.APKCodeAnalyzer._run_androguard")
    @patch.object(
        __import__("redb.extractors.decompiler.apk.apktool_wrapper", fromlist=["ApktoolDisassembler"]).ApktoolDisassembler,
        "disassemble",
        return_value=False,
    )
    @patch.object(
        __import__("redb.extractors.decompiler.apk.jadx_wrapper", fromlist=["JADXDecompiler"]).JADXDecompiler,
        "decompile",
        return_value=False,
    )
    def test_extract_returns_dict_keys(self, mock_jadx, mock_apktool, mock_androguard):
        from redb.extractors.decompiler.apk.analyzer import APKCodeAnalyzer
        mock_analysis = MagicMock()
        mock_analysis.get_methods.return_value = []
        mock_androguard.return_value = (MagicMock(), [], mock_analysis)

        analyzer = APKCodeAnalyzer("/fake/path.apk", log=MagicMock())
        results = analyzer.extract()

        assert "decompiled_content" in results
        assert "decompiled_refs" in results
        assert "smali_content" in results
        assert "smali_refs" in results
        assert "similarity_metrics" in results
        assert "strings" in results
        assert "analysis_errors" in results
        analyzer.cleanup()

    @patch("redb.extractors.decompiler.apk.analyzer.APKCodeAnalyzer._run_androguard")
    def test_extract_androguard_failure(self, mock_androguard):
        from redb.extractors.decompiler.apk.analyzer import APKCodeAnalyzer
        mock_androguard.side_effect = Exception("Analysis failed")

        analyzer = APKCodeAnalyzer("/fake/path.apk", log=MagicMock())
        results = analyzer.extract()

        assert len(results["analysis_errors"]) > 0
        analyzer.cleanup()

    def test_cleanup_removes_temp_dirs(self):
        from redb.extractors.decompiler.apk.analyzer import APKCodeAnalyzer
        analyzer = APKCodeAnalyzer("/fake/path.apk")
        tmpdir = tempfile.mkdtemp()
        analyzer._temp_dirs.append(tmpdir)
        assert os.path.isdir(tmpdir)
        analyzer.cleanup()
        assert not os.path.isdir(tmpdir)

    def test_normalize_java(self):
        from redb.extractors.decompiler.apk.analyzer import _normalize_java
        source = "  public void foo() {\n    int x = 1;\n  }\n"
        normalized = _normalize_java(source)
        assert normalized == "public void foo() {\nint x = 1;\n}"


class TestNormalizeAndroguardOperands:
    """Tests for androguard-to-apktool output normalization."""

    def test_invoke_adds_braces(self):
        from redb.extractors.decompiler.apk.analyzer import _normalize_androguard_operands
        result = _normalize_androguard_operands(
            "invoke-direct",
            "v1, Ljava/lang/Object;-><init>()V"
        )
        assert result == "{v1}, Ljava/lang/Object;-><init>()V"

    def test_invoke_virtual_multiple_regs(self):
        from redb.extractors.decompiler.apk.analyzer import _normalize_androguard_operands
        result = _normalize_androguard_operands(
            "invoke-virtual",
            "v0, v1, Ljava/lang/String;->equals(Ljava/lang/Object;)Z"
        )
        assert result == "{v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z"

    def test_invoke_static_no_regs(self):
        from redb.extractors.decompiler.apk.analyzer import _normalize_androguard_operands
        # invoke-static with no register args (rare but possible)
        result = _normalize_androguard_operands(
            "invoke-static",
            "Ljava/lang/System;->gc()V"
        )
        # First part starts with L, so no registers to wrap
        assert result == "Ljava/lang/System;->gc()V"

    def test_invoke_range(self):
        from redb.extractors.decompiler.apk.analyzer import _normalize_androguard_operands
        result = _normalize_androguard_operands(
            "invoke-virtual/range",
            "v3, v4, v5, Landroid/util/Log;->d(Ljava/lang/String; Ljava/lang/String;)I"
        )
        assert result == "{v3, v4, v5}, Landroid/util/Log;->d(Ljava/lang/String; Ljava/lang/String;)I"

    def test_iget_field_colon(self):
        from redb.extractors.decompiler.apk.analyzer import _normalize_androguard_operands
        result = _normalize_androguard_operands(
            "iget-object",
            "v0, v4, LF;->a Ljava/lang/String;"
        )
        assert result == "v0, v4, LF;->a:Ljava/lang/String;"

    def test_iput_field_colon(self):
        from redb.extractors.decompiler.apk.analyzer import _normalize_androguard_operands
        result = _normalize_androguard_operands(
            "iput-object",
            "v0, v1, LA;->b Ljava/lang/String;"
        )
        assert result == "v0, v1, LA;->b:Ljava/lang/String;"

    def test_sget_primitive_field(self):
        from redb.extractors.decompiler.apk.analyzer import _normalize_androguard_operands
        result = _normalize_androguard_operands(
            "sget",
            "v0, Lcom/Foo;->count I"
        )
        assert result == "v0, Lcom/Foo;->count:I"

    def test_sput_array_field(self):
        from redb.extractors.decompiler.apk.analyzer import _normalize_androguard_operands
        result = _normalize_androguard_operands(
            "sput-object",
            "v0, Lcom/Foo;->data [B"
        )
        assert result == "v0, Lcom/Foo;->data:[B"

    def test_non_invoke_non_field_passthrough(self):
        from redb.extractors.decompiler.apk.analyzer import _normalize_androguard_operands
        result = _normalize_androguard_operands(
            "const-string",
            'v0, "hello world"'
        )
        assert result == 'v0, "hello world"'

    def test_move_passthrough(self):
        from redb.extractors.decompiler.apk.analyzer import _normalize_androguard_operands
        result = _normalize_androguard_operands("move", "v0, v4")
        assert result == "v0, v4"

    def test_goto_passthrough(self):
        from redb.extractors.decompiler.apk.analyzer import _normalize_androguard_operands
        result = _normalize_androguard_operands("goto", "+005h")
        assert result == "+005h"


class TestSmaliCFG:
    """Tests for smali CFG construction and metrics computation."""

    def test_empty_body(self):
        from redb.extractors.decompiler.apk.smali_cfg import compute_cfg_metrics
        m = compute_cfg_metrics("")
        assert m.block_count == 0
        assert m.cyclomatic_complexity == 1

    def test_linear_method_apktool(self):
        """Single basic block — no branches."""
        from redb.extractors.decompiler.apk.smali_cfg import compute_cfg_metrics
        smali = textwrap.dedent("""\
            .registers 2
            invoke-direct {v1}, Ljava/lang/Object;-><init>()V
            const-string v0, "hello"
            iput-object v0, v1, LA;->a:Ljava/lang/String;
            return-void
        """)
        m = compute_cfg_metrics(smali)
        assert m.block_count == 1
        assert m.edge_count == 0
        assert m.cyclomatic_complexity == 1
        assert m.loop_count == 0
        assert m.max_depth == 0
        assert m.max_fan_out == 0

    def test_linear_method_androguard(self):
        """Single basic block — androguard format (no labels/directives)."""
        from redb.extractors.decompiler.apk.smali_cfg import compute_cfg_metrics
        smali = (
            "    invoke-direct {v1}, Ljava/lang/Object;-><init>()V\n"
            "    const-string v0, \"hello\"\n"
            "    iput-object v0, v1, LA;->a:Ljava/lang/String;\n"
            "    return-void"
        )
        m = compute_cfg_metrics(smali)
        assert m.block_count == 1
        assert m.edge_count == 0
        assert m.cyclomatic_complexity == 1

    def test_if_branch_apktool(self):
        """Diamond pattern: if-else with two paths merging."""
        from redb.extractors.decompiler.apk.smali_cfg import compute_cfg_metrics
        smali = textwrap.dedent("""\
            .registers 2
            const/4 v0, 0x0
            if-eqz v0, :cond_0
            const/4 v0, 0x1
            goto :goto_0
            :cond_0
            const/4 v0, 0x2
            :goto_0
            return v0
        """)
        m = compute_cfg_metrics(smali)
        # Blocks: [const,if] [const,goto] [const] [return]
        assert m.block_count == 4
        assert m.cyclomatic_complexity == 2  # E - N + 2
        assert m.loop_count == 0
        assert m.max_fan_out == 2  # if-branch has 2 successors

    def test_loop_apktool(self):
        """Simple loop: goto back to earlier label."""
        from redb.extractors.decompiler.apk.smali_cfg import compute_cfg_metrics
        smali = textwrap.dedent("""\
            .registers 2
            const/4 v0, 0x0
            :loop_start
            add-int/lit8 v0, v0, 0x1
            const/16 v1, 0xa
            if-lt v0, v1, :loop_start
            return v0
        """)
        m = compute_cfg_metrics(smali)
        assert m.loop_count == 1  # back edge from if-lt to loop_start
        assert m.cyclomatic_complexity >= 2

    def test_if_branch_androguard(self):
        """If-else in androguard offset format."""
        from redb.extractors.decompiler.apk.smali_cfg import compute_cfg_metrics
        smali = (
            "    const/4 v0, 0x0\n"
            "    if-eqz v0, +2h\n"        # skip 2 instructions ahead
            "    const/4 v0, 0x1\n"
            "    goto +1h\n"               # skip 1 ahead
            "    const/4 v0, 0x2\n"
            "    return v0"
        )
        m = compute_cfg_metrics(smali)
        assert m.block_count >= 3
        assert m.cyclomatic_complexity >= 2

    def test_multiple_if_branches(self):
        """Multiple if-branches increase cyclomatic complexity."""
        from redb.extractors.decompiler.apk.smali_cfg import compute_cfg_metrics
        smali = textwrap.dedent("""\
            .registers 3
            if-eqz v0, :cond_0
            if-eqz v1, :cond_1
            const/4 v2, 0x0
            goto :end
            :cond_0
            const/4 v2, 0x1
            goto :end
            :cond_1
            const/4 v2, 0x2
            :end
            return v2
        """)
        m = compute_cfg_metrics(smali)
        # Two if-branches: CC should be >= 3
        assert m.cyclomatic_complexity >= 3

    def test_return_terminates_block(self):
        """Return instruction terminates block with no successor."""
        from redb.extractors.decompiler.apk.smali_cfg import compute_cfg_metrics
        smali = textwrap.dedent("""\
            .registers 1
            const/4 v0, 0x0
            return v0
        """)
        m = compute_cfg_metrics(smali)
        assert m.block_count == 1
        assert m.edge_count == 0

    def test_throw_terminates_block(self):
        """Throw instruction terminates block with no successor."""
        from redb.extractors.decompiler.apk.smali_cfg import compute_cfg_metrics
        smali = textwrap.dedent("""\
            .registers 1
            new-instance v0, Ljava/lang/RuntimeException;
            invoke-direct {v0}, Ljava/lang/RuntimeException;-><init>()V
            throw v0
        """)
        m = compute_cfg_metrics(smali)
        assert m.block_count == 1
        assert m.edge_count == 0

    def test_max_depth_linear_chain(self):
        """Chain of blocks gives increasing depth."""
        from redb.extractors.decompiler.apk.smali_cfg import compute_cfg_metrics
        # 3 blocks chained: entry -> if -> fall-through -> return
        smali = textwrap.dedent("""\
            .registers 2
            const/4 v0, 0x0
            if-nez v0, :cond_0
            const/4 v0, 0x1
            :cond_0
            return v0
        """)
        m = compute_cfg_metrics(smali)
        assert m.max_depth >= 1

    def test_nested_loop_apktool(self):
        """Nested loop: two back edges."""
        from redb.extractors.decompiler.apk.smali_cfg import compute_cfg_metrics
        smali = textwrap.dedent("""\
            .registers 3
            const/4 v0, 0x0
            :outer
            const/4 v1, 0x0
            :inner
            add-int/lit8 v1, v1, 0x1
            if-lt v1, v2, :inner
            add-int/lit8 v0, v0, 0x1
            if-lt v0, v2, :outer
            return-void
        """)
        m = compute_cfg_metrics(smali)
        assert m.loop_count == 2

    def test_consistency_formula(self):
        """Verify E - N + 2 formula matches cyclomatic_complexity."""
        from redb.extractors.decompiler.apk.smali_cfg import compute_cfg_metrics
        smali = textwrap.dedent("""\
            .registers 2
            const/4 v0, 0x0
            if-eqz v0, :cond_0
            const/4 v0, 0x1
            goto :goto_0
            :cond_0
            const/4 v0, 0x2
            :goto_0
            return v0
        """)
        m = compute_cfg_metrics(smali)
        expected_cc = m.edge_count - m.block_count + 2
        assert m.cyclomatic_complexity == max(1, expected_cc)