Olivier C. L. Haas

25 papers C 1Journal 11Unranked 13
YearRankTypeTitle / Venue / Authors
2024 J jnl
IEEE Trans. Veh. Technol.
Farah Mahdi Al-Sallami, Fatma Benkhelifa, Danah Ashour, Zabih Ghassemlooy, Olivier C. L. Haas, Zahir Ahmad, Sujan Rajbhandari
2024 conf
CAMAD
Sina Ebrahimi, Faouzi Bouali, Olivier C. L. Haas
2024 J jnl
Sensors
Sidharth Bhanu Kamtam, Qian Lu, Faouzi Bouali, Olivier C. L. Haas, Stewart A. Birrell
2024 J jnl
IEEE Commun. Surv. Tutorials
Sina Ebrahimi, Faouzi Bouali, Olivier C. L. Haas
2023 J jnl
IEEE Trans. Control. Syst. Technol.
Zihao Liu, Arash Moradinegade Dizqah, Jose M. Herreros, Joschka Schaub, Olivier C. L. Haas
2023 conf
ICAC
Iyai Davies, Olivier C. L. Haas, C. Paul Amadi, C. Royal Ateke, Seminiworima Akuro
2022 J jnl
IEEE Syst. J.
Farah M. Alsalami, Olivier C. L. Haas, Ahmed Al-Kinani, Cheng-Xiang Wang, Zahir Ahmad, Sujan Rajbhandari
2021 J jnl
Veh. Commun.
Farah M. Alsalami, Zahir Ahmad, Stanislav Zvanovec, Paul Anthony Haigh, Olivier C. L. Haas, Sujan Rajbhandari
2020 conf
CSNDSP
Abdulrahman A. Mahmoud, Zahir Ahmad, Yousef Almadani, Muhammad Ijaz, Olivier C. L. Haas, Sujan Rajbhandari
2020 conf
CSNDSP
Farah M. Alsalami, Zahir Ahmad, Paul Anthony Haigh, Olivier C. L. Haas, Sujan Rajbhandari
2019 conf
ICAC
Iyai Davies, Olivier C. L. Haas
2019 J jnl
Sensors
Farah M. Alsalami, Zahir Ahmad, Stanislav Zvanovec, Paul Anthony Haigh, Olivier C. L. Haas, Sujan Rajbhandari
2019 conf
ICM
M. Kalliris, Stratis Kanarachos, Rigas Kotsakis, Olivier C. L. Haas, Mike Blundell
2017 J jnl
Veh. Commun.
Madeline Cheah, Siraj A. Shaikh, Olivier C. L. Haas, Alastair R. Ruddle
2015 J jnl
Eur. J. Control
Iyai Davies, Olivier C. L. Haas
2015 conf
ECC
Iyai Davies, Olivier C. L. Haas
2014 J jnl
IEEE Trans. Control. Syst. Technol.
Daniel Paluszczyszyn, Piotr Skworcow, Olivier C. L. Haas, Keith J. Burnham, John A. Mills
2012 conf
ITSC
Pawel Jaworski, Tim Edwards, Keith J. Burnham, Olivier C. L. Haas
2011 conf
ITSC
Olivier C. L. Haas
2011 conf
ITSC
Shoaib Kamran, Olivier C. L. Haas
2007 C conf
ACC
Devi Putra, Piotr Skworcow, Olivier C. L. Haas, Keith J. Burnham, John A. Mills
2006 J jnl
Pattern Recognit.
J. H. Goodband, Olivier C. L. Haas, John A. Mills
2004 conf
DAGM-Symposium
Weibing Xu, Saad Ali Amin, Olivier C. L. Haas, Keith J. Burnham, John A. Mills
2003 conf
ICANNGA
S. Martineau, Elena I. Gaura, Keith J. Burnham, Olivier C. L. Haas
1995 conf
ICANNGA
Olivier C. L. Haas, Keith J. Burnham, M. H. Fisher, John A. Mills
tests/unit/test_apk_jadx_wrapper.py
← Index tests/unit/test_apk_jadx_wrapper.py python
"""
Unit tests for JADX wrapper subprocess management and output parsing.

All subprocess calls are mocked — no JADX/Java installation required.
"""
import os
import tempfile
import textwrap

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

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


class TestJADXDecompiler:
    """Tests for JADXDecompiler subprocess wrapper."""

    @patch.dict(os.environ, {}, clear=False)
    def test_init_defaults(self):
        os.environ.pop("JADX_PATH", None)
        from redb.extractors.decompiler.apk.jadx_wrapper import JADXDecompiler
        j = JADXDecompiler()
        assert j.jadx_path == "jadx"
        assert j.timeout == 300

    def test_init_custom_path(self):
        from redb.extractors.decompiler.apk.jadx_wrapper import JADXDecompiler
        j = JADXDecompiler(jadx_path="/usr/local/bin/jadx", timeout=300)
        assert j.jadx_path == "/usr/local/bin/jadx"
        assert j.timeout == 300

    def test_init_from_env(self):
        from redb.extractors.decompiler.apk.jadx_wrapper import JADXDecompiler
        with patch.dict(os.environ, {"JADX_PATH": "/opt/jadx/bin/jadx", "JADX_TIMEOUT": "120"}):
            j = JADXDecompiler()
            assert j.jadx_path == "/opt/jadx/bin/jadx"
            assert j.timeout == 120

    @patch("subprocess.Popen")
    def test_decompile_success(self, mock_popen):
        from redb.extractors.decompiler.apk.jadx_wrapper import JADXDecompiler
        mock_proc = MagicMock()
        mock_proc.communicate.return_value = ("output", "")
        mock_proc.returncode = 0
        mock_popen.return_value = mock_proc

        j = JADXDecompiler()
        result = j.decompile("/test.apk", "/output")
        assert result is True

    @patch("subprocess.Popen")
    def test_decompile_nonzero_exit_no_sources(self, mock_popen):
        from redb.extractors.decompiler.apk.jadx_wrapper import JADXDecompiler
        mock_proc = MagicMock()
        mock_proc.communicate.return_value = ("", "error msg")
        mock_proc.returncode = 1
        mock_popen.return_value = mock_proc

        j = JADXDecompiler(log=MagicMock())
        result = j.decompile("/test.apk", "/nonexistent_output")
        assert result is False

    @patch("subprocess.Popen")
    def test_decompile_timeout(self, mock_popen):
        from redb.extractors.decompiler.apk.jadx_wrapper import JADXDecompiler
        import subprocess
        mock_proc = MagicMock()
        mock_proc.communicate.side_effect = subprocess.TimeoutExpired(cmd="jadx", timeout=10)
        mock_proc.pid = 12345
        mock_popen.return_value = mock_proc

        with patch("os.getpgid", return_value=12345), \
             patch("os.killpg"):
            j = JADXDecompiler(timeout=10, log=MagicMock())
            result = j.decompile("/test.apk", "/output")
            assert result is False

    @patch("subprocess.Popen", side_effect=FileNotFoundError)
    def test_decompile_not_found(self, mock_popen):
        from redb.extractors.decompiler.apk.jadx_wrapper import JADXDecompiler
        j = JADXDecompiler(log=MagicMock())
        result = j.decompile("/test.apk", "/output")
        assert result is False

    @patch("subprocess.Popen")
    def test_decompile_command_args(self, mock_popen):
        from redb.extractors.decompiler.apk.jadx_wrapper import JADXDecompiler
        mock_proc = MagicMock()
        mock_proc.communicate.return_value = ("", "")
        mock_proc.returncode = 0
        mock_popen.return_value = mock_proc

        j = JADXDecompiler(jadx_path="/opt/jadx")
        j.decompile("/test.apk", "/output")

        cmd = mock_popen.call_args[0][0]
        assert cmd[0] == "/opt/jadx"
        assert "--no-res" in cmd
        assert "--no-imports" in cmd
        assert "--threads-count" in cmd
        assert "/test.apk" in cmd

    def test_parse_java_methods_empty_dir(self):
        from redb.extractors.decompiler.apk.jadx_wrapper import JADXDecompiler
        j = JADXDecompiler()
        result = j.parse_java_methods("/nonexistent")
        assert result == {}

    def test_parse_java_methods_with_files(self):
        from redb.extractors.decompiler.apk.jadx_wrapper import JADXDecompiler
        with tempfile.TemporaryDirectory() as tmpdir:
            sources_dir = os.path.join(tmpdir, "sources", "com", "example")
            os.makedirs(sources_dir)

            java_content = textwrap.dedent("""\
                package com.example;

                public class MyClass {
                    public void doStuff(int x) {
                        System.out.println(x);
                    }

                    private String getName() {
                        return "test";
                    }
                }
            """)
            with open(os.path.join(sources_dir, "MyClass.java"), "w") as f:
                f.write(java_content)

            j = JADXDecompiler()
            methods = j.parse_java_methods(tmpdir)
            assert len(methods) >= 1

    def test_extract_methods_from_java(self):
        from redb.extractors.decompiler.apk.jadx_wrapper import JADXDecompiler
        source = textwrap.dedent("""\
            public void foo(int x) {
                if (x > 0) {
                    System.out.println(x);
                }
            }
        """)
        j = JADXDecompiler()
        methods = j._extract_methods_from_java(source, "com.example.Test")
        assert len(methods) == 1
        key = list(methods.keys())[0]
        assert "foo" in key

    def test_is_method_declaration_valid(self):
        from redb.extractors.decompiler.apk.jadx_wrapper import JADXDecompiler
        j = JADXDecompiler()
        result = j._is_method_declaration("    public void foo(int x) {")
        assert result is not None
        assert result[0] == "foo"

    def test_is_method_declaration_control_flow(self):
        from redb.extractors.decompiler.apk.jadx_wrapper import JADXDecompiler
        j = JADXDecompiler()
        # 'if', 'for', etc. should not be recognized as methods
        assert j._is_method_declaration("        if (x > 0) {") is None
        assert j._is_method_declaration("        for (int i = 0; i < 10; i++) {") is None

    def test_is_method_declaration_static(self):
        from redb.extractors.decompiler.apk.jadx_wrapper import JADXDecompiler
        j = JADXDecompiler()
        result = j._is_method_declaration("    public static void main(String[] args) {")
        assert result is not None
        assert result[0] == "main"