aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/test_android_downloads.py
blob: 96da9a44398677dc6077d04d00576a046e42e26d (plain) (blame)
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
"""
Downloads in the Android application, pinned by reading the source.

The page's contract with a native save target is platform.js `nativeSave`: a
sink with write/close/abort, an `open` only when the target can open the file,
and nothing that names a path. The Android sink keeps it; what it may open is
downloads.js `OPENABLE`, held equal here because a second copy of a list of
safe types is how an `.html` gets opened one day.
"""

import re
from pathlib import Path

import pytest
from spa_source import STATIC

PACKAGES = Path(__file__).resolve().parents[2]
MAIN = PACKAGES / "meshbay-android" / "app" / "src" / "main"
SAVE = MAIN / "kotlin" / "org" / "meshbay" / "client" / "save"
SHIM = MAIN / "assets" / "bridge" / "meshbay-bridge.js"

pytestmark = pytest.mark.skipif(not SAVE.exists(), reason="android sources not present")


def _read(path: Path) -> str:
    return path.read_text(encoding="utf-8")


def test_what_may_be_opened_is_the_pages_list():
    js = _read(STATIC / "downloads.js").split("const OPENABLE = {", 1)[1].split("};", 1)[0]
    kt = _read(SAVE / "SaveNames.kt").split("val OPENABLE = mapOf(", 1)[1].split("\n    )", 1)[0]
    page = {k: v.split(";")[0] for k, v in re.findall(r"(\w+): '([^']+)'", js)}
    android = dict(re.findall(r'"(\w+)" to "([^"]+)"', kt))
    assert page and android == page, set(android.items()) ^ set(page.items())


def test_open_is_offered_only_where_the_target_says_so():
    shim = _read(SHIM)
    save = shim.split("saveFile: async", 1)[1].split("\n    },", 1)[0]
    assert "if (handle.openable) sink.open" in save
    sinks = _read(SAVE / "SaveSinks.kt")
    assert '.put("openable", SaveNames.openableType(shown) != null)' in sinks
    opening = sinks.split("fun open(id: Long)", 1)[1].split("\n    }", 1)[0]
    assert "SaveNames.openableType(name) ?: throw Refused" in opening


def test_the_page_is_told_names_never_uris():
    sinks = _read(SAVE / "SaveSinks.kt")
    for fn in ("fun chooseFolder", "fun getFolder", "fun begin"):
        body = sinks.split(fn, 1)[1].split("\n    fun ", 1)[0]
        returned = re.findall(r'put\("(\w+)"', body)
        assert not {"uri", "path", "tree"} & set(returned), (fn, returned)
    assert "return displayName(treeDocument(tree))" in sinks


def test_the_save_channels_are_the_desktops():
    preload = _read(PACKAGES / "meshbay-client" / "src" / "preload.js")
    shim = _read(SHIM)

    def channels(text: str, call: str) -> set[str]:
        return set(re.findall(rf"{call}\('((?:save|folder):[\w:-]+)'", text))

    assert channels(shim, "call") == channels(preload, r"ipcRenderer\.invoke")
    assert "nativeSave: true" in shim


def test_an_unfinished_file_never_carries_the_final_name():
    sinks = _read(SAVE / "SaveSinks.kt")
    assert '"$wanted.part"' in sinks
    assert "MediaStore.MediaColumns.IS_PENDING, 1" in sinks
    assert "MediaStore.MediaColumns.IS_PENDING, 0" in sinks
    abort = sinks.split("fun abort(id: Long)", 1)[1].split("\n    }", 1)[0]
    assert "delete(sink.uri)" in abort
    assert "fun cleanUpAfterAKilledProcess" in sinks
    assert "saves.cleanUpAfterAKilledProcess()" in _read(
        MAIN / "kotlin" / "org" / "meshbay" / "client" / "MainActivity.kt")


def test_a_chosen_folder_that_has_gone_is_not_silently_replaced():
    begin = _read(SAVE / "SaveSinks.kt").split("fun begin(", 1)[1].split("\n    fun ", 1)[0]
    assert "auto && !(configuredTree != null && tree == null)" in begin


def test_writes_are_binary_and_awaited():
    shim = _read(SHIM)
    assert "head.setUint32(0, 0x4d424231)" in shim
    assert "port.postMessage(frame)" in shim
    bridge = _read(MAIN / "kotlin" / "org" / "meshbay" / "client" / "bridge" / "Bridge.kt")
    before = bridge.split("TYPE_ARRAY_BUFFER", 1)[0]
    assert "!isMainFrame" in before, "a binary message must pass the same sender check"