Jacek M. Zmojda

12 papers Journal 3Unranked 9
YearRankTypeTitle / Venue / Authors
2023 J jnl
Sensors
Pawel Awramiuk, Karolina Sadowska, Jaroslaw Wiater, Dariusz Sajewicz, Marcin Kochanowicz, Wojciech Walendziuk, Jacek M. Zmojda
2021 J jnl
Sensors
Marcin Kochanowicz, Jacek M. Zmojda, Agata Baranowska, Marta Kuwik, Bartlomiej Starzyk, Magdalena Lesniak, Piotr Miluski, Wojciech A. Pisarski, Joanna Pisarska, Jan Dorosz, Maurizio Ferrari, Dominik Dorosz
2021 J jnl
Sensors
Agata Baranowska, Marcin Kochanowicz, Aleksandra Wajda, Magdalena Lesniak, Jacek M. Zmojda, Piotr Miluski, Izabela Zglobicka, Krzysztof J. Kurzydlowski, Dominik Dorosz
2019 conf
ICTON
Marcin Kochanowicz, Jacek M. Zmojda, Agata Baranowska, Piotr Miluski, Magdalena Lesniak, Marta Kuwik, Joanna Pisarska, Wojciech A. Pisarski, Maciej Sitarz, Leonid Mochalov, Aleksey Nezhdanov, Maurizio Ferrari, Giancarlo Righini, Jan Dorosz, Dominik Dorosz
2018 conf
ICTON
Marcin Kochanowicz, Jacek M. Zmojda, Piotr Miluski, Dariusz Mazur, Agata Baranowska, Marta Soltys, Wojciech A. Pisarski, Joanna Pisarska, Tomasz Ragin, Maurizio Ferrari, Giancarlo Righini, Matthias Jager, Maciej Sitarz, Dominik Dorosz
2018 conf
ICTON
Renata Jadach, Marcin Kochanowicz, Jacek M. Zmojda, Agata Baranowska, Piotr Miluski, Magdalena Lesniak, Marta Soltys, Joanna Pisarska, Wojciech A. Pisarski, Anna Lukowiak, Maciej Sitarz, Leonid Mochalov, Aleksey Nezhdanov, Maurizio Ferrari, Giancarlo Righini, Dominik Dorosz
2017 conf
ICTON
Tomasz Ragin, Jacek M. Zmojda, Marcin Kochanowicz, Renata Jadach, Piotr Miluski, Dominik Dorosz
2017 conf
ICTON
Jacek M. Zmojda, Marcin Kochanowicz, Piotr Miluski, Renata Jadach, Wojciech A. Pisarski, Joanna Pisarska, Maurizio Ferrari, Giancarlo Righini, Dominik Dorosz
2016 conf
ICTON
Martyna Kowal, Joanna Pisarska, Marcin Kochanowicz, Jacek M. Zmojda, Jan Dorosz, Dominik Dorosz, Wojciech A. Pisarski
2016 conf
ICTON
Jacek M. Zmojda, Marcin Kochanowicz, Piotr Miluski, Anna Lukowiak, Wojciech A. Pisarski, Joanna Pisarska, Marian Marciniak, Maurizio Ferrari, Giancarlo Righini, Maciej Sitarz, Dominik Dorosz
2016 conf
ICTON
Marcin Kochanowicz, Jacek M. Zmojda, Piotr Miluski, P. Jelen, Maciej Sitarz, Dominik Dorosz
2015 conf
ICTON
Dominik Dorosz, Marcin Kochanowicz, Jacek M. Zmojda, Piotr Miluski, Marian Marciniak, Alessandro Chiasera, Andrea Chiappini, Iustyna Vasilchenko, Maurizio Ferrari, Giancarlo Righini
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"