Kai Nickel

24 papers A 1B 3Journal 3Unranked 15
YearRankTypeTitle / Venue / Authors
2009 ch.
Computers in the Human Interaction Loop
Kai Nickel, Montse Pardàs, Rainer Stiefelhagen, Cristian Canton-Ferrer, José Luis Landabaso, Josep R. Casas
2008 conf
ECCV (4)
Kai Nickel, Rainer Stiefelhagen
2008
Kai Nickel
2007 J jnl
IEEE Trans. Robotics
Rainer Stiefelhagen, Hazim Kemal Ekenel, Christian Fügen, Petra Gieselmann, Hartwig Holzapfel, Florian Kraft, Kai Nickel, Michael Voit, Alex Waibel
2007 conf
Humanoids
Kai Nickel, Rainer Stiefelhagen
2007 conf
CLEAR
Michael Voit, Kai Nickel, Rainer Stiefelhagen
2007 J jnl
Image Vis. Comput.
Kai Nickel, Rainer Stiefelhagen
2006 conf
MFI
Michael Voit, Kai Nickel, Rainer Stiefelhagen
2006 conf
MFI
Christian Wojek, Kai Nickel, Rainer Stiefelhagen
2006 conf
CLEAR
Kai Nickel, Tobias Gehrig, Hazim Kemal Ekenel, John W. McDonough, Rainer Stiefelhagen
2006 J jnl
Signal Process.
Rainer Stiefelhagen, Keni Bernardin, Hazim Kemal Ekenel, John W. McDonough, Kai Nickel, Michael Voit, Matthias Wölfel
2006 conf
CLEAR
Michael Voit, Kai Nickel, Rainer Stiefelhagen
2006 conf
CVPR (1)
Julius Ziegler, Kai Nickel, Rainer Stiefelhagen
2005 B conf
ICMI
Kai Nickel, Tobias Gehrig, Rainer Stiefelhagen, John W. McDonough
2005 conf
MLMI
Michael Voit, Kai Nickel, Rainer Stiefelhagen
2005 conf
MLMI
Matthias Wölfel, Kai Nickel, John W. McDonough
2005 conf
CRV
Michael Voit, Kai Nickel, Rainer Stiefelhagen
2004 conf
FGR
Kai Nickel, Edgar Seemann, Rainer Stiefelhagen
2004 conf
FGR
Edgar Seemann, Kai Nickel, Rainer Stiefelhagen
2004 B conf
ICMI
Hartwig Holzapfel, Kai Nickel, Rainer Stiefelhagen
2004 A conf
IROS
Rainer Stiefelhagen, Christian Fügen, Petra Gieselmann, Hartwig Holzapfel, Kai Nickel, Alex Waibel
2004 conf
ECCV Workshop on HCI
Kai Nickel, Rainer Stiefelhagen
2003 B conf
ICMI
Kai Nickel, Rainer Stiefelhagen
2003 conf
DAGM-Symposium
Kai Nickel, Rainer Stiefelhagen
tests/unit/test_apk_apktool_wrapper.py
← Index tests/unit/test_apk_apktool_wrapper.py python
"""
Unit tests for apktool wrapper subprocess management and smali directory detection.

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

import pytest
from unittest.mock import MagicMock, patch

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


class TestApktoolDisassembler:
    """Tests for ApktoolDisassembler subprocess wrapper."""

    @patch.dict(os.environ, {}, clear=False)
    def test_init_defaults(self):
        os.environ.pop("APKTOOL_PATH", None)
        from redb.extractors.decompiler.apk.apktool_wrapper import ApktoolDisassembler
        a = ApktoolDisassembler()
        assert a.apktool_path == "apktool"
        assert a.timeout == 120

    def test_init_custom(self):
        from redb.extractors.decompiler.apk.apktool_wrapper import ApktoolDisassembler
        a = ApktoolDisassembler(apktool_path="/opt/apktool", timeout=120)
        assert a.apktool_path == "/opt/apktool"
        assert a.timeout == 120

    def test_init_from_env(self):
        from redb.extractors.decompiler.apk.apktool_wrapper import ApktoolDisassembler
        with patch.dict(os.environ, {"APKTOOL_PATH": "/usr/bin/apktool", "APKTOOL_TIMEOUT": "90"}):
            a = ApktoolDisassembler()
            assert a.apktool_path == "/usr/bin/apktool"
            assert a.timeout == 90

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

        a = ApktoolDisassembler()
        result = a.disassemble("/test.apk", "/output")
        assert result is True

    @patch("subprocess.Popen")
    def test_disassemble_failure(self, mock_popen):
        from redb.extractors.decompiler.apk.apktool_wrapper import ApktoolDisassembler
        mock_proc = MagicMock()
        mock_proc.communicate.return_value = ("", "error")
        mock_proc.returncode = 1
        mock_popen.return_value = mock_proc

        a = ApktoolDisassembler(log=MagicMock())
        result = a.disassemble("/test.apk", "/output")
        assert result is False

    @patch("subprocess.Popen")
    def test_disassemble_timeout(self, mock_popen):
        from redb.extractors.decompiler.apk.apktool_wrapper import ApktoolDisassembler
        import subprocess
        mock_proc = MagicMock()
        mock_proc.communicate.side_effect = subprocess.TimeoutExpired(cmd="apktool", timeout=10)
        mock_proc.pid = 12345
        mock_popen.return_value = mock_proc

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

    @patch("subprocess.Popen", side_effect=FileNotFoundError)
    def test_disassemble_not_found(self, mock_popen):
        from redb.extractors.decompiler.apk.apktool_wrapper import ApktoolDisassembler
        a = ApktoolDisassembler(log=MagicMock())
        result = a.disassemble("/test.apk", "/output")
        assert result is False

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

        a = ApktoolDisassembler(apktool_path="/opt/apktool")
        a.disassemble("/test.apk", "/output")

        cmd = mock_popen.call_args[0][0]
        assert cmd[0] == "/opt/apktool"
        assert "d" in cmd
        assert "--no-res" in cmd
        assert "--force" in cmd
        assert "/test.apk" in cmd

    def test_get_smali_directories_single_dex(self):
        from redb.extractors.decompiler.apk.apktool_wrapper import ApktoolDisassembler
        with tempfile.TemporaryDirectory() as tmpdir:
            os.makedirs(os.path.join(tmpdir, "smali"))
            a = ApktoolDisassembler()
            dirs = a.get_smali_directories(tmpdir)
            assert len(dirs) == 1
            assert dirs[0].endswith("smali")

    def test_get_smali_directories_multi_dex(self):
        from redb.extractors.decompiler.apk.apktool_wrapper import ApktoolDisassembler
        with tempfile.TemporaryDirectory() as tmpdir:
            os.makedirs(os.path.join(tmpdir, "smali"))
            os.makedirs(os.path.join(tmpdir, "smali_classes2"))
            os.makedirs(os.path.join(tmpdir, "smali_classes3"))
            # Should not be included
            os.makedirs(os.path.join(tmpdir, "res"))
            os.makedirs(os.path.join(tmpdir, "original"))

            a = ApktoolDisassembler()
            dirs = a.get_smali_directories(tmpdir)
            assert len(dirs) == 3

    def test_get_smali_directories_empty(self):
        from redb.extractors.decompiler.apk.apktool_wrapper import ApktoolDisassembler
        with tempfile.TemporaryDirectory() as tmpdir:
            a = ApktoolDisassembler()
            dirs = a.get_smali_directories(tmpdir)
            assert dirs == []

    def test_get_smali_directories_nonexistent(self):
        from redb.extractors.decompiler.apk.apktool_wrapper import ApktoolDisassembler
        a = ApktoolDisassembler()
        dirs = a.get_smali_directories("/nonexistent/path")
        assert dirs == []