Randy Gomez

180 papers A* 21A 22B 36C 3Misc 5Journal 42Unranked 51
YearRankTypeTitle / Venue / Authors
2026 A* conf
HRI
Zhennan Yi, Sophia Sakakibara Capello, Randy Gomez, Selma Sabanovic
2026 J jnl
CoRR
Zhennan Yi, Sophia Sakakibara Capello, Randy Gomez, Selma Sabanovic
2026 conf
HRI Companion
Eric Nichols, Miguel Mejia Tobar, Randy Gomez
2026 J jnl
IEEE Robotics Autom. Lett.
Pengyu Zhao, Zheng Fang, Tongxu Ai, Eric Nichols, Randy Gomez, Bo He, Guangliang Li
2025 J jnl
IEEE Access
Edwin C. Montiel-Vázquez, Christian Arzate Cruz, Jorge Adolfo Ramírez Uresti, Miguel González-Mendoza, Randy Gomez
2025 B conf
RO-MAN
Anneloes L. Meijer, Julie Pivin-Bachler, Gloria Alvarez-Benito, Gabriel Amores Carredano, Randy Gomez, Egon L. van den Broek
2025 B conf
RO-MAN
Zhennan Yi, Leigh Levinson, Diego Delgado-Chaves, Jose M. Perez-Moleron, Nabil Bougria, Antonia Krummheuer, Matthias Rehm, Anders Kalsgaard Møller, Katrine Kielsholm Ramsgaard, Selma Auala, Heike Winschiers-Theophilus, Edward Nepolo, David Calero, Devis Dal Moro, Daniel Serrano, Magí Dalmau-Moreno, Randy Gomez, Luis Merino, Selma Sabanovic
2025 B conf
Graphics Interface
Eranga De Saa, Denise Alonso-Vázquez, Charles-Olivier Dufresne Camaro, Yumiko Sakamoto, Javier M. Antelis, Randy Gomez, Pourang Irani
2025 A* conf
HRI
Randy Gomez
2025 J jnl
Frontiers Robotics AI
Casey Kennington, Koji Inoue, Randy Gomez, Peter Ford Dominey
2025 A* conf
HRI
Christian Arzate Cruz, Edwin C. Montiel-Vázquez, Chikara Maeda, Darryl Lam, Randy Gomez
2025 conf
ICSR+AI (3)
Gonzalo A. García, Rohan Laycock, Guillermo Pérez, J. Gabriel Amores, Gloria Alvarez-Benito, Manuel Castro-Malet, Randy Gomez
2025 J jnl
Proc. ACM Hum. Comput. Interact.
Leigh Levinson, Gloria Alvarez-Benito, J. Gabriel Amores, Deborah Szapiro, Randy Gomez, Selma Sabanovic
2025 B conf
RO-MAN
Lu Cao, Tung Nguyen, Paul Reisert, Eric Nichols, Chikara Maeda, Darryl Lam, Sarah Rose Siskind, Randy Gomez
2025 conf
ROBIO
Yang Chen, Mingyang Hu, Liu Tian, Eric Nichols, Randy Gomez, Guangliang Li
2025 J jnl
Int. J. Child Comput. Interact.
Leigh Levinson, Vicky Charisi, Chris Zotos, Randy Gomez, Selma Sabanovic
2025 J jnl
Frontiers Robotics AI
Seshagopalan Thorapalli Muralidharan, Randy Gomez, Georgios Andrikopoulos
2025 A conf
IROS
Fei Tang, Mingyang Hu, Yu Fang, Hongqi Yu, Eric Nichols, Randy Gomez, Guangliang Li
2025 conf
ICSR+AI (1)
Liu Tian, Yang Chen, Mingyang Hu, Eric Nichols, Randy Gomez, Guangliang Li
2025 conf
ICSR+AI (2)
Antonio Cano, Guillermo Pérez, Luis Merino, Randy Gomez
2025 B conf
RO-MAN
Christian Arzate Cruz, Edwin C. Montiel-Vázquez, Chikara Maeda, Randy Gomez
2025 J jnl
CoRR
Christian Arzate Cruz, Edwin C. Montiel-Vázquez, Chikara Maeda, Randy Gomez
2024 conf
ICSR + AI (2)
Randy Gomez, Eric Nichols, Yu Fang, Serge Thill, Álvaro Páez, Luis Merino
2024 conf
Robophilosophy
Leigh Levinson, Eli B. McGraw, Randy Gomez, Selma Sabanovic
2024 J jnl
Adv. Robotics
Guillermo Pérez-García, Gonzalo A. García, Manuel Castro-Malet, Mario Castaño, Marta J. López-González de Quevedo, Ricardo Durán, Luis Pérez, J. Gabriel Amores, Gloria Alvarez-Benito, Leigh M. Levinson, Selma Sabanovic, Levko Ivanchuk, Randy Gomez
2024 conf
HRI (Companion)
Zining Wang, Paul Reisert, Eric Nichols, Randy Gomez
2024 J jnl
CoRR
Zining Wang, Paul Reisert, Eric Nichols, Randy Gomez
2024 A* conf
ICRA
Fei Tang, Chuanxiong Zheng, Hongqi Yu, Lei Zhang, Eric Nichols, Randy Gomez, Guangliang Li
2024 A conf
IROS
Lei Zhang, Chuanxiong Zheng, Hui Wang, Randy Gomez, Eric Nichols, Guangliang Li
2024 B conf
RO-MAN
Leigh Levinson, Randy Gomez, Selma Sabanovic
2024 B conf
RO-MAN
Christian Arzate Cruz, Yotam Sechayk, Takeo Igarashi, Randy Gomez
2024 J jnl
CoRR
Christian Arzate Cruz, Yotam Sechayk, Takeo Igarashi, Randy Gomez
2024 A* conf
ICRA
Randy Gomez, Deborah Szapiro, Sara Cooper, Nabil Bougria, Guillermo Pérez, Eric Nichols, Javier Giménez-Figueroa, Jose M. Perez-Moleron, Matthew Peavy, Daniel Serrano, Luis Merino
2024 conf
HRI (Companion)
Sara Cooper, Randy Gomez, Deborah Szapiro, Luis Merino
2024 conf
HRI (Companion)
Matous Jelínek, Eric Nichols, Randy Gomez
2024 J jnl
CoRR
Matous Jelínek, Eric Nichols, Randy Gomez
2024 J jnl
IEEE Access
Edwin C. Montiel-Vázquez, Christian Arzate Cruz, Jorge Adolfo Ramírez Uresti, Randy Gomez
2024 B conf
RO-MAN
Yu Fang, Jose M. Perez-Moleron, Luis Merino, Randy Gomez
2024 J jnl
Adv. Robotics
Yu Fang, Jose M. Perez-Moleron, Luis Merino, Su-Ling Yeh, Shigeaki Nishina, Randy Gomez
2024 J jnl
J. Smart Cities Soc.
Julie Pivin-Bachler, Egon L. van den Broek, Randy Gomez
2024 B conf
RO-MAN
Sara Cooper, Randy Gomez, Deborah Szapiro, Luis Merino
2024 conf
ROBIO
Hongqi Yu, Fei Tang, Lei Zhang, Randy Gomez, Eric Nichols, Guangliang Li
2024 conf
AIM
Algot Lindestam, Seshagopalan Thorapalli Muralidharan, Georgios Andrikopoulos, Randy Gomez
2024 conf
AIM
Georgios Andrikopoulos, Lars Hässler, Randy Gomez
2024 J jnl
Frontiers Robotics AI
Leigh Levinson, Jessica McKinney, Christena Nippert-Eng, Randy Gomez, Selma Sabanovic
2024 J jnl
Proc. ACM Interact. Mob. Wearable Ubiquitous Technol.
Francisco Perella-Holfeld, Samar Sallam, Julia Petrie, Randy Gomez, Pourang Irani, Yumiko Sakamoto
2024 J jnl
Adv. Robotics
Gonzalo A. García, Guillermo Pérez, Rohan K. Laycock-Narayan, Leigh Levinson, J. Gabriel Amores, Gloria Alvarez-Benito, Manuel Castro-Malet, Mario Castaño-Ocaña, Marta J. López-González de Quevedo, Ricardo Durán-Viñuelas, Randy Gomez, Selma Sabanovic
2024 A* conf
ICRA
Chuanxiong Zheng, Lei Zhang, Hui Wang, Randy Gomez, Eric Nichols, Guangliang Li
2024 A* conf
HRI
Leigh Levinson, Christena Nippert-Eng, Randy Gomez, Selma Sabanovic
2024 conf
HRI (Companion)
Leigh Levinson, Tyler Barrett, Randy Gomez, Selma Sabanovic
2024 conf
HRI (Companion)
Leigh Levinson, Zachary Kaufman, Arinah Karim, Andrew Huang, Randy Gomez, Selma Sabanovic
2024 conf
ICSR + AI (3)
Randy Gomez, Eleanor Sandry, Selma Sabanovic, Deborah Szapiro, Vicky Charisi, Daniel Serrano, Thomas H. Weisswange, Matthew P. Aylett, Guangliang Li, Pourang Irani, Eric Nichols, Luis Merino
2024 J jnl
IEEE Robotics Autom. Lett.
Wei Meng, Hao Ju, Tongxu Ai, Randy Gomez, Eric Nichols, Guangliang Li
2024 J jnl
Biomed. Signal Process. Control.
Yannick Benezeth, Deepak Krishnamoorthy, Deivid Botina-Monsalve, Keisuke Nakamura, Randy Gomez, Johel Mitéran
2024 J jnl
Frontiers Robotics AI
Sarah Rose Siskind, Eric Nichols, Randy Gomez
2023 B conf
Intelligent Environments (Workshops)
Julie Pivin-Bachler, Randy Gomez, Egon L. van den Broek
2023 conf
CUI
Matthew Peter Aylett, Andrea Carmantini, Christopher J. Pidcock, Eric Nichols, Randy Gomez
2023 J jnl
CoRR
Tung Nguyen, Eric Nichols, Randy Gomez
2023 conf
SENSORS
Dennis Laurijssen, Anthony Schenck, Girmi Schouten, Robin Kerstens, Sebastiaan Aussems, Eric Paillet, Randy Gomez, Keisuke Nakamura, Walter Daems, Jan Steckel
2023 B conf
RO-MAN
Guillermo Pérez, Gonzalo A. García, Manuel Castro-Malet, Mario Castaño, Marta J. López-González de Quevedo, Ricardo Durán, J. Gabriel Amores, Gloria Alvarez-Benito, Leigh Levinson, Selma Sabanovic, Randy Gomez
2023 B conf
RO-MAN
Yu Fang, Luis Merino, Serge Thill, Randy Gomez
2023 J jnl
J. Smart Cities Soc.
Anneloes L. Meijer, Lukas P. A. Arts, Randy Gomez, Egon L. van den Broek
2023 conf
ICSR (2)
Lei Zhang, Chuanxiong Zheng, Hui Wang, Eric Nichols, Randy Gomez, Guangliang Li
2023 B conf
MUM
Anuradha Herath, Samar Sallam, Yumiko Sakamoto, Randy Gomez, Pourang Irani
2023 A* conf
ICRA
Jie Huang, Jiangshan Hao, Rongshun Juan, Randy Gomez, Keisuke Nakamura, Guangliang Li
2023 conf
HRI (Companion)
Matthew P. Aylett, Andrea Carmantini, Christopher J. Pidcock, Eric Nichols, Randy Gomez, Sarah Rose Siskind
2023 B conf
HAI
Yumiko Sakamoto, Anuradha Herath, Tanvi Vuradi, Samar Sallam, Randy Gomez, Pourang Irani
2023 A conf
IROS
Eric Nichols, Deborah Szapiro, Yurii Vasylkiv, Randy Gomez
2023 B conf
RO-MAN
Gonzalo A. García, Guillermo Pérez, Leigh Levinson, J. Gabriel Amores, Gloria Alvarez-Benito, Manuel Castro-Malet, Mario Castaño-Ocaña, Marta J. López-González de Quevedo, Ricardo Durán-Viñuelas, Randy Gomez, Selma Sabanovic
2023 A conf
IROS
Jie Huang, Jiangshan Hao, Rongshun Juan, Randy Gomez, Keisuke Nakarnura, Guangliang Li
2023 B conf
HAI
Anuradha Herath, Samar Sallam, Tanvi Vuradi, Yumiko Sakamoto, Randy Gomez, Pourang Irani
2023 A* conf
ICRA
Rongshun Juan, Hao Ju, Jie Huang, Randy Gomez, Keisuke Nakamura, Guangliang Li
2023 J jnl
CoRR
Manith Adikari, Angelo Cangelosi, Randy Gomez
2023 B conf
Intelligent Environments (Workshops)
Anneloes L. Meijer, Lukas P. A. Arts, Randy Gomez, Egon L. van den Broek
2023 conf
CHI Extended Abstracts
Matthew Peter Aylett, Randy Gomez, Eleanor Sandry, Selma Sabanovic
2022 A conf
IROS
Hui Wang, Jinying Lin, Zhen Ma, Yurii Vasylkiv, Heike Brock, Keisuke Nakamura, Randy Gomez, Bo He, Guangliang Li
2022 A* conf
ICRA
Randy Gomez, Álvaro Páez, Yu Fang, Serge Thill, Luis Merino, Eric Nichols, Keisuke Nakamura, Heike Brock
2022 A conf
IROS
Eric Nichols, Sarah Rose Siskind, Levko Ivanchuk, Guillermo Pérez, Waki Kamino, Selma Sabanovic, Randy Gomez
2022 B conf
RO-MAN
Eric Nichols, Deborah Szapiro, Yurii Vasylkiv, Randy Gomez
2022 conf
ICSR (2)
Chuanxiong Zheng, Hui Wang, Lei Zhang, Jiangshan Hao, Randy Gomez, Keisuke Nakamura, Guangliang Li
2022 conf
ICSR (2)
Leigh Levinson, Gonzalo A. García, Guillermo Pérez, Gloria Alvarez-Benito, J. Gabriel Amores, Mario Castaño-Ocaña, Manuel Castro-Malet, Randy Gomez, Selma Sabanovic
2022 Misc conf
ICASSP
Heike Brock, Randy Gomez
2022 conf
ICSR (1)
Ricardo Ragel, Rafael Rey, Álvaro Páez, Javier Ponce Chulani, Keisuke Nakamura, Fernando Caballero, Luis Merino, Randy Gomez
2022 conf
ICSR (2)
Hui Wang, Lei Zhang, Chuanxiong Zheng, Randy Gomez, Keisuke Nakamura, Guangliang Li
2022 B conf
RO-MAN
Hui Wang, Guodong Chen, Randy Gomez, Keisuke Nakamura, Bo He, Guangliang Li
2022 B conf
RO-MAN
Kerl Galindo, Deborah Szapiro, Randy Gomez
2022 J jnl
Nat. Mac. Intell.
Hao Ju, Rongshun Juan, Randy Gomez, Keisuke Nakamura, Guangliang Li
2021 J jnl
Frontiers Robotics AI
Eleanor Sandry, Randy Gomez, Keisuke Nakamura
2021 A* conf
ICRA
Yurii Vasylkiv, Zhen Ma, Guangliang Li, Eleanor Sandry, Heike Brock, Keisuke Nakamura, Pourang Irani, Randy Gomez
2021 A conf
IROS
Eric Nichols, Leo Gao, Yurii Vasylkiv, Randy Gomez
2021 J jnl
Frontiers Comput. Sci.
Eric Nichols, Leo Gao, Yurii Vasylkiv, Randy Gomez
2021 B conf
ICSR
Yurii Vasylkiv, Ricardo Ragel, Javier Ponce Chulani, Luis Merino, Eleanor Sandry, Heike Brock, Keisuke Nakamura, Pourang Irani, Randy Gomez
2021 conf
HRI (Companion)
Vicky Charisi, Selma Sabanovic, Angelo Cangelosi, Randy Gomez
2021 B conf
ICSR
Randy Gomez, Yu Fang, Serge Thill, Ricardo Ragel, Heike Brock, Keisuke Nakamura, Yurii Vasylkiv, Eric Nichols, Luis Merino
2021 B conf
RO-MAN
Marvin Brenner, Heike Brock, Andreas Stiegler, Randy Gomez
2021 B conf
RO-MAN
Randy Gomez, Deborah Szapiro, Kerl Galindo, Luis Merino, Heike Brock, Keisuke Nakamura, Yu Fang, Eric Nichols
2021 Misc conf
IDC
Vicky Charisi, Tomoko Imai, Tiija Rinta, Joy Maliza Nakhayenze, Randy Gomez
2021 J jnl
CoRR
Jie Huang, Rongshun Juan, Randy Gomez, Keisuke Nakamura, Qixin Sha, Bo He, Guangliang Li
2021 J jnl
IEEE Access
Serge Bobbia, Richard Macwan, Yannick Benezeth, Keisuke Nakamura, Randy Gomez, Julien Dubois
2021 B conf
ICSR
Eric Nichols, Sarah Rose Siskind, Waki Kamino, Selma Sabanovic, Randy Gomez
2021 A conf
IROS
Heike Brock, Randy Gomez
2021 conf
HRI (Companion)
Heike Brock, Selma Sabanovic, Randy Gomez
2021 B conf
RO-MAN
Yurii Vasylkiv, Zhen Ma, Guangliang Li, Heike Brock, Keisuke Nakamura, Pourang Irani, Randy Gomez
2021 A conf
IROS
Rongshun Juan, Jie Huang, Randy Gomez, Keisuke Nakamura, Qixin Sha, Bo He, Guangliang Li
2021 A* conf
ICRA
Vicky Charisi, Luis Merino, Marina Escobar, Fernando Caballero, Randy Gomez, Emilia Gómez
2020 A* conf
ICRA
Randy Gomez, Deborah Szapiro, Luis Merino, Keisuke Nakamura
2020 J jnl
IEEE Access
Jinying Lin, Zhen Ma, Randy Gomez, Keisuke Nakamura, Bo He, Guangliang Li
2020 conf
CVPR Workshops
Duncan Luguern, Simon Perche, Yannick Benezeth, Virginie Moser, L. Andrea Dunbar, Fabian Braun, Alia Lemkaddem, Keisuke Nakamura, Randy Gomez, Julien Dubois
2020 B conf
ICSR
Yurii Vasylkiv, Heike Brock, Yu Fang, Eric Nichols, Keisuke Nakamura, Serge Thill, Randy Gomez
2020 J jnl
Frontiers Robotics AI
Vicky Charisi, Emilia Gómez, Gonzalo Mier, Luis Merino, Randy Gomez
2020 C conf
MIG
Eric Nichols, Leo Gao, Randy Gomez
2020 J jnl
CoRR
Eric Nichols, Leo Gao, Randy Gomez
2020 conf
HRI (Companion)
Michael Suguitan, Randy Gomez, Guy Hoffman
2020 J jnl
IEEE Access
Heike Brock, Javier Ponce Chulani, Luis Merino, Deborah Szapiro, Randy Gomez
2020 B conf
ICSR
Randy Gomez, Deborah Szapiro, Luis Merino, Heike Brock, Keisuke Nakamura, Selma Sabanovic
2020 conf
HRI (Companion)
Randy Gomez, Selma Sabanovic, Angelo Cangelosi, Luis Merino, Keisuke Nakamura
2020 B conf
RO-MAN
Jinying Lin, Qilei Zhang, Randy Gomez, Keisuke Nakamura, Bo He, Guangliang Li
2020 conf
CVPR Workshops
Deivid Botina-Monsalve, Yannick Benezeth, Richard Macwan, Paul Pierrart, Federico Parra, Keisuke Nakamura, Randy Gomez, Johel Mitéran
2020 A* conf
HRI
Michael Suguitan, Randy Gomez, Guy Hoffman
2020 conf
ISMICT
Duncan Luguern, Yannick Benezeth, Virginie Moser, L. Andrea Dunbar, Fabian Braun, Alia Lemkaddem, Keisuke Nakamura, Randy Gomez, Julien Dubois
2020 B conf
RO-MAN
Heike Brock, Selma Sabanovic, Keisuke Nakamura, Randy Gomez
2020 B conf
ICSR
Swapna Joshi, Sawyer Collins, Waki Kamino, Randy Gomez, Selma Sabanovic
2020 J jnl
Sensors
Peixi Li, Yannick Benezeth, Richard Macwan, Keisuke Nakamura, Randy Gomez, Chao Li, Fan Yang
2019 conf
ICIAP (1)
Reda Belaiche, Rita Meziati Sabour, Cyrille Migniot, Yannick Benezeth, Dominique Ginhac, Keisuke Nakamura, Randy Gomez, Fan Yang
2019 A* conf
HRI
Vicky Charisi, Selma Sabanovic, Serge Thill, Emilia Gómez, Keisuke Nakamura, Randy Gomez
2019 B conf
RO-MAN
Gonzalo Mier, Fernando Caballero, Keisuke Nakamura, Luis Merino, Randy Gomez
2019 J jnl
IEEE Trans. Hum. Mach. Syst.
Guangliang Li, Randy Gomez, Keisuke Nakamura, Bo He
2019 J jnl
CoRR
Guangliang Li, Randy Gomez, Keisuke Nakamura, Jinying Lin, Qilei Zhang, Bo He
2019 conf
VISIGRAPP (4: VISAPP)
Peixi Li, Yannick Benezeth, Keisuke Nakamura, Randy Gomez, Fan Yang
2019 conf
ISMICT
Yannick Benezeth, Serge Bobbia, Keisuke Nakamura, Randy Gomez, Julien Dubois
2019 conf
ITCH
Yurii Vasylkiv, Ali Neshati, Yumiko Sakamoto, Randy Gomez, Keisuke Nakamura, Pourang Irani
2018 J jnl
J. Syst. Archit.
Chao Li, Yannick Benezeth, Keisuke Nakamura, Randy Gomez, Fan Yang
2018 C conf
BIBE
Peixi Li, Yannick Benezeth, Keisuke Nakamura, Randy Gomez, Chao Li, Fan Yang
2018 A* conf
HRI
Randy Gomez, Deborah Szapiro, Kerl Galindo, Keisuke Nakamura
2018 B conf
RO-MAN
Guangliang Li, Bo He, Randy Gomez, Keisuke Nakamura
2018 A* conf
CHI
Teng Han, Jiannan Li, Khalad Hasan, Keisuke Nakamura, Randy Gomez, Ravin Balakrishnan, Pourang Irani
2018 conf
BHI
Richard Macwan, Yannick Benezeth, Keisuke Nakamura, Randy Gomez, Yadong Wu, Alamin Mansouri
2018 conf
CVPR Workshops
Serge Bobbia, Duncan Luguern, Yannick Benezeth, Keisuke Nakamura, Randy Gomez, Julien Dubois
2018 conf
BHI
Yannick Benezeth, Peixi Li, Richard Macwan, Keisuke Nakamura, Randy Gomez, Fan Yang
2018 conf
IWAENC
Keisuke Nakamura, Randy Gomez
2017 conf
EUSIPCO
Chao Li, Yannick Benezeth, Keisuke Nakamura, Randy Gomez, Fan Yang
2017 conf
CHI Extended Abstracts
Cosmin Munteanu, Pourang Irani, Sharon L. Oviatt, Matthew P. Aylett, Gerald Penn, Shimei Pan, Nikhil Sharma, Frank Rudzicz, Randy Gomez, Benjamin R. Cowan, Keisuke Nakamura
2017 B conf
RO-MAN
Randy Gomez, Keisuke Nakamura
2017 C conf
ASRU
Keisuke Nakamura, Randy Gomez
2017 A* conf
UIST
Teng Han, Khalad Hasan, Keisuke Nakamura, Randy Gomez, Pourang Irani
2016 B conf
LREC
Nurul Lubis, Randy Gomez, Sakriani Sakti, Keisuke Nakamura, Koichiro Yoshino, Satoshi Nakamura, Kazuhiro Nakadai
2016 conf
CHI Extended Abstracts
Cosmin Munteanu, Pourang Irani, Sharon L. Oviatt, Matthew P. Aylett, Gerald Penn, Shimei Pan, Nikhil Sharma, Frank Rudzicz, Randy Gomez, Keisuke Nakamura, Kazuhiro Nakadai
2016 conf
Humanoids
Randy Gomez, Keisuke Nakamura
2016 B conf
RO-MAN
Randy Gomez, Yurii Vasylkiv, Keisuke Nakamura, Takeshi Mizumoto, Kazuhiro Nakadai
2015 conf
Humanoids
Randy Gomez, Keisuke Nakamura, Takeshi Mizumoto, Kazuhiro Nakadai
2015 A conf
INTERSPEECH
Randy Gomez, Levko Ivanchuk, Keisuke Nakamura, Takeshi Mizumoto, Kazuhiro Nakadai
2015 A* conf
ICRA
Randy Gomez, Keisuke Nakamura, Takeshi Mizumoto, Kazuhiro Nakadai
2015 A conf
IROS
Randy Gomez, Levko Ivanchuk, Keisuke Nakamura, Takeshi Mizumoto, Kazuhiro Nakadai
2014 conf
HSCMA
Randy Gomez, Keisuke Nakamura, Takeshi Mizumoto, Kazuhiro Nakadai
2014 J jnl
IEEE Trans. Hum. Mach. Syst.
Tony Tung, Randy Gomez, Tatsuya Kawahara, Takashi Matsuyama
2014 A conf
IROS
Randy Gomez, Koji Inoue, Keisuke Nakamura, Takeshi Mizumoto, Kazuhiro Nakadai
2013 A conf
IROS
Randy Gomez, Keisuke Nakamura, Kazuhiro Nakadai
2013 A* conf
ICRA
Randy Gomez, Keisuke Nakamura, Kazuhiro Nakadai, Ui-Hyun Kim, Hiroshi G. Okuno, Tatsuya Kawahara
2013 conf
Humanoids
Randy Gomez, Keisuke Nakamura, Takeshi Mizumoto, Kazuhiro Nakadai
2013 conf
HCI (4)
Tony Tung, Randy Gomez, Tatsuya Kawahara, Takashi Matsuyama
2013 A conf
IROS
Keisuke Nakamura, Randy Gomez, Kazuhiro Nakadai
2013 Misc conf
ICASSP
Randy Gomez, Keisuke Nakamura, Kazuhiro Nakadai
2012 A conf
INTERSPEECH
Tatsuya Kawahara, Randy Gomez
2012 conf
ECCV Workshops (1)
Tony Tung, Randy Gomez, Tatsuya Kawahara, Takashi Matsuyama
2012 A* conf
HRI
Randy Gomez, Tatsuya Kawahara, Keisuke Nakamura, Kazuhiro Nakadai
2011 A conf
INTERSPEECH
Randy Gomez, Tatsuya Kawahara
2010 A conf
INTERSPEECH
Randy Gomez, Tatsuya Kawahara
2010 Misc conf
ICASSP
Randy Gomez, Tatsuya Kawahara
2010 J jnl
IEEE Trans. Speech Audio Process.
Randy Gomez, Tatsuya Kawahara
2010 conf
Humanoids
Randy Gomez, Tatsuya Kawahara, Kazuhiro Nakadai
2009 A conf
INTERSPEECH
Randy Gomez, Tatsuya Kawahara
2009 J jnl
Speech Commun.
Randy Gomez, Tomoki Toda, Hiroshi Saruwatari, Kiyohiro Shikano
2008 Misc conf
ICASSP
Randy Gomez, Jani Even, Hiroshi Saruwatari, Kiyohiro Shikano
2008 A conf
INTERSPEECH
Randy Gomez, Jani Even, Kiyohiro Shikano
2007 A conf
INTERSPEECH
Randy Gomez, Tomoki Toda, Hiroshi Saruwatari, Kiyohiro Shikano
2007 J jnl
IEICE Trans. Inf. Syst.
Randy Gomez, Tomoki Toda, Hiroshi Saruwatari, Kiyohiro Shikano
2006 conf
ICASSP (1)
Randy Gomez, Tomoki Toda, Hiroshi Saruwatari, Kiyohiro Shikano
2006 J jnl
IEICE Trans. Inf. Syst.
Randy Gomez, Akinobu Lee, Tomoki Toda, Hiroshi Saruwatari, Kiyohiro Shikano
2005 A conf
INTERSPEECH
Randy Gomez, Akinobu Lee, Hiroshi Saruwatari, Kiyohiro Shikano
2004 A conf
INTERSPEECH
Randy Gomez, Akinobu Lee, Hiroshi Saruwatari, Kiyohiro Shikano
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)