1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
|
from __future__ import annotations
import sys
from typing import TYPE_CHECKING, Any
from unittest.mock import MagicMock
import pytest
from platformdirs.android import Android
if TYPE_CHECKING:
from pytest_mock import MockerFixture
@pytest.mark.parametrize(
"params",
[
{},
{"appname": "foo"},
{"appname": "foo", "appauthor": "bar"},
{"appname": "foo", "appauthor": "bar", "version": "v1.0"},
{"appname": "foo", "appauthor": "bar", "version": "v1.0", "opinion": False},
],
ids=[
"no_args",
"app_name",
"app_name_with_app_author",
"app_name_author_version",
"app_name_author_version_false_opinion",
],
)
def test_android(mocker: MockerFixture, params: dict[str, Any], func: str) -> None:
mocker.patch("platformdirs.android._android_folder", return_value="/data/data/com.example", autospec=True)
mocker.patch("platformdirs.android.os.path.join", lambda *args: "/".join(args))
result = getattr(Android(**params), func)
suffix_elements = []
if "appname" in params:
suffix_elements.append(params["appname"])
if "version" in params:
suffix_elements.append(params["version"])
if suffix_elements:
suffix_elements.insert(0, "")
suffix = "/".join(suffix_elements)
val = "/tmp" # noqa: S108
expected_map = {
"user_data_dir": f"/data/data/com.example/files{suffix}",
"site_data_dir": f"/data/data/com.example/files{suffix}",
"user_config_dir": f"/data/data/com.example/shared_prefs{suffix}",
"site_config_dir": f"/data/data/com.example/shared_prefs{suffix}",
"user_cache_dir": f"/data/data/com.example/cache{suffix}",
"site_cache_dir": f"/data/data/com.example/cache{suffix}",
"user_state_dir": f"/data/data/com.example/files{suffix}",
"user_log_dir": f"/data/data/com.example/cache{suffix}{'' if params.get('opinion', True) is False else '/log'}",
"user_documents_dir": "/storage/emulated/0/Documents",
"user_downloads_dir": "/storage/emulated/0/Downloads",
"user_pictures_dir": "/storage/emulated/0/Pictures",
"user_videos_dir": "/storage/emulated/0/DCIM/Camera",
"user_music_dir": "/storage/emulated/0/Music",
"user_desktop_dir": "/storage/emulated/0/Desktop",
"user_runtime_dir": f"/data/data/com.example/cache{suffix}{'' if not params.get('opinion', True) else val}",
"site_runtime_dir": f"/data/data/com.example/cache{suffix}{'' if not params.get('opinion', True) else val}",
}
expected = expected_map[func]
assert result == expected
def test_android_folder_from_jnius(mocker: MockerFixture, monkeypatch: pytest.MonkeyPatch) -> None:
from platformdirs import PlatformDirs # noqa: PLC0415
from platformdirs.android import _android_folder # noqa: PLC0415
mocker.patch.dict(sys.modules, {"android": MagicMock(side_effect=ModuleNotFoundError)})
monkeypatch.delitem(__import__("sys").modules, "android")
_android_folder.cache_clear()
if PlatformDirs is Android: # type: ignore[comparison-overlap] # See https://github.com/platformdirs/platformdirs/pull/295
import jnius # pragma: no cover # noqa: PLC0415
autoclass = mocker.spy(jnius, "autoclass") # pragma: no cover
else:
parent = MagicMock(return_value=MagicMock(getAbsolutePath=MagicMock(return_value="/A"))) # pragma: no cover
context = MagicMock(getFilesDir=MagicMock(return_value=MagicMock(getParentFile=parent))) # pragma: no cover
autoclass = MagicMock(return_value=context) # pragma: no cover
mocker.patch.dict(sys.modules, {"jnius": MagicMock(autoclass=autoclass)}) # pragma: no cover
result = _android_folder()
assert result == "/A"
assert autoclass.call_count == 1
assert autoclass.call_args[0] == ("android.content.Context",)
assert _android_folder() is result
assert autoclass.call_count == 1
def test_android_folder_from_p4a(mocker: MockerFixture, monkeypatch: pytest.MonkeyPatch) -> None:
from platformdirs.android import _android_folder # noqa: PLC0415
mocker.patch.dict(sys.modules, {"jnius": MagicMock(side_effect=ModuleNotFoundError)})
monkeypatch.delitem(__import__("sys").modules, "jnius")
_android_folder.cache_clear()
get_absolute_path = MagicMock(return_value="/A")
get_parent_file = MagicMock(getAbsolutePath=get_absolute_path)
get_files_dir = MagicMock(getParentFile=MagicMock(return_value=get_parent_file))
get_application_context = MagicMock(getFilesDir=MagicMock(return_value=get_files_dir))
m_activity = MagicMock(getApplicationContext=MagicMock(return_value=get_application_context))
mocker.patch.dict(sys.modules, {"android": MagicMock(mActivity=m_activity)})
result = _android_folder()
assert result == "/A"
assert get_absolute_path.call_count == 1
assert _android_folder() is result
assert get_absolute_path.call_count == 1
@pytest.mark.parametrize(
"path",
[
"/data/user/1/a/files",
"/data/data/a/files",
"/mnt/expand/8e06fc2f-a86a-44e8-81ce-109e0eedd5ed/user/1/a/files",
],
)
def test_android_folder_from_sys_path(mocker: MockerFixture, path: str, monkeypatch: pytest.MonkeyPatch) -> None:
mocker.patch.dict(sys.modules, {"jnius": MagicMock(side_effect=ModuleNotFoundError)})
monkeypatch.delitem(__import__("sys").modules, "jnius")
mocker.patch.dict(sys.modules, {"android": MagicMock(side_effect=ModuleNotFoundError)})
monkeypatch.delitem(__import__("sys").modules, "android")
from platformdirs.android import _android_folder # noqa: PLC0415
_android_folder.cache_clear()
monkeypatch.setattr(sys, "path", ["/A", "/B", path])
result = _android_folder()
assert result == path[: -len("/files")]
def test_android_folder_not_found(mocker: MockerFixture, monkeypatch: pytest.MonkeyPatch) -> None:
mocker.patch.dict(sys.modules, {"jnius": MagicMock(autoclass=MagicMock(side_effect=ModuleNotFoundError))})
from platformdirs.android import _android_folder # noqa: PLC0415
_android_folder.cache_clear()
monkeypatch.setattr(sys, "path", [])
assert _android_folder() is None
|