diff --git a/tests/test_frame_apk_versions.py b/tests/test_frame_apk_versions.py index 8cf7701..53f1511 100644 --- a/tests/test_frame_apk_versions.py +++ b/tests/test_frame_apk_versions.py @@ -256,5 +256,28 @@ class UploadVersionsTest(unittest.TestCase): ssh.assert_not_called() + def test_blocked_uploads_are_reported_like_failed_installs(self): + import server + info = {'package': 'org.example.app', 'label': 'Example', 'version': '5', + 'version_code': 5, 'min_sdk': 33, 'abis': [], 'icon_png': None} + for apk_info, expected_info in ((dict(info), 'org.example.app'), + (frame_android.FrameError('not an APK'), None)): + handler = object.__new__(server.Handler) + handler.headers = {'X-Filename': 'app.apk', 'X-Mode': 'apk', 'Content-Length': '1'} + handler.rfile = io.BytesIO(b'x') + calls = [] + patch_info = (patch.object(frame_android, 'apk_info', side_effect=apk_info) + if isinstance(apk_info, Exception) else + patch.object(frame_android, 'apk_info', return_value=apk_info)) + with patch_info, patch.object(frame_android, 'install_hooks', [lambda *a: calls.append(a)]), \ + patch.object(server, 'ensure_master'): + with self.assertRaises(server.Failure): + handler.upload() + self.assertEqual(len(calls), 1) + got_info, meta, error, _ = calls[0] + self.assertEqual((got_info or {}).get('package'), expected_info) + self.assertIsNone(meta) + self.assertIsInstance(error, frame_android.FrameError) + if __name__ == '__main__': unittest.main() diff --git a/ui/index.html b/ui/index.html index 82e6437..92ec81a 100644 --- a/ui/index.html +++ b/ui/index.html @@ -1545,6 +1545,7 @@ function showApkAlternatives(reason, result) { if (res) $("apkAltDlg").close(); else { b.disabled = false; b.textContent = "Install"; } await loadAndroid(); if (cat.apps) { const y = window.scrollY; filterCatalog(); window.scrollTo(0, y); } + if (res && res.app) await offerTest(res.app); }; if (!$("apkAltDlg").open) $("apkAltDlg").showModal(); } diff --git a/ui/server.py b/ui/server.py index 8a04aa0..23aa600 100755 --- a/ui/server.py +++ b/ui/server.py @@ -1595,13 +1595,18 @@ class Handler(BaseHTTPRequestHandler): keep = True # stage_title owns tmp now, and removes it on failure return stage_title(str(dest), temp_dir=str(tmp)) if mode == "apk": + # Checked here, before install(), to hand the page a blocker it can offer + # alternatives for; report these failures the way install() would have. + start = time.time() try: info = frame_android.apk_info(str(dest)) except frame_android.FrameError as e: + frame_android._after_install(None, None, e, start) raise Failure(str(e), 400) try: frame_android.check_installable(info) except frame_android.FrameError as e: + frame_android._after_install(info, None, e, start) raise Failure(str(e), 400, {"package": info["package"], "version_code": info.get("version_code"), "blocker": str(e)}) ensure_master() try: