Order list of PDF viewers and return default application first (Linux).

This commit is contained in:
Ro 2024-06-10 11:45:31 -04:00 committed by Alexis Métaireau
parent f03bc71855
commit 54ab9ce98f
No known key found for this signature in database
GPG key ID: C65C7A89A8FFC56E
2 changed files with 131 additions and 5 deletions

View file

@ -4,6 +4,7 @@ import platform
import shlex
import subprocess
import typing
from collections import OrderedDict
from pathlib import Path
from typing import Dict, Optional
@ -49,7 +50,7 @@ class DangerzoneGui(DangerzoneCore):
# Preload font
self.fixed_font = QtGui.QFontDatabase.systemFont(QtGui.QFontDatabase.FixedFont)
# Preload list of PDF viewers on computer
# Preload ordered list of PDF viewers on computer, starting with default
self.pdf_viewers = self._find_pdf_viewers()
# Are we done waiting (for Docker Desktop to be installed, or for container to install)
@ -93,9 +94,22 @@ class DangerzoneGui(DangerzoneCore):
log.info(Fore.YELLOW + "> " + Fore.CYAN + args_str)
subprocess.Popen(args)
def _find_pdf_viewers(self) -> Dict[str, str]:
pdf_viewers: Dict[str, str] = {}
def _find_pdf_viewers(self) -> OrderedDict[str, str]:
pdf_viewers: OrderedDict[str, str] = OrderedDict()
if platform.system() == "Linux":
# Opportunistically query for default pdf handler
default_pdf_viewer = None
try:
default_pdf_viewer = subprocess.check_output(
["xdg-mime", "query", "default", "application/pdf"]
).decode()
except (FileNotFoundError, subprocess.CalledProcessError) as e:
# Log it and continue
log.info(
"xdg-mime query failed, default PDF handler could not be found."
)
log.debug(f"xdg-mime query failed: {e}")
# Find all .desktop files
for search_path in [
"/usr/share/applications",
@ -108,14 +122,26 @@ class DangerzoneGui(DangerzoneCore):
if os.path.splitext(filename)[1] == ".desktop":
# See which ones can open PDFs
desktop_entry = DesktopEntry(full_filename)
desktop_entry_name = desktop_entry.getName()
if (
"application/pdf" in desktop_entry.getMimeTypes()
and "dangerzone" not in desktop_entry.getName().lower()
and "dangerzone" not in desktop_entry_name.lower()
):
pdf_viewers[desktop_entry.getName()] = (
pdf_viewers[desktop_entry_name] = (
desktop_entry.getExec()
)
# Put the default entry first
if filename == default_pdf_viewer:
try:
pdf_viewers.move_to_end(
desktop_entry_name, last=False
)
except KeyError as e:
# Should be unreachable
log.error(
f"Problem reordering applications: {e}"
)
except FileNotFoundError:
pass

100
tests/gui/test_logic.py Normal file
View file

@ -0,0 +1,100 @@
import platform
import subprocess
from unittest import mock
import pytest
from dangerzone.gui.logic import DangerzoneGui
if platform.system() == "Linux":
from xdg.DesktopEntry import DesktopEntry
@pytest.mark.skipif(platform.system() != "Linux", reason="Linux-only test")
def test_order_mime_handers() -> None:
"""
Given a default mime handler returned by xdg-mime,
ensure it is the first item available in the list
of compatible applications.
"""
mock_app = mock.MagicMock()
dummy = mock.MagicMock()
mock_desktop = mock.MagicMock(spec=DesktopEntry)
mock_desktop.getMimeTypes.return_value = "application/pdf"
mock_desktop.getExec.side_effect = [
"/usr/bin/madeup-evince",
"/usr/local/bin/madeup-mupdf",
"/usr/local/bin/madeup-libredraw",
]
mock_desktop.getName.side_effect = [
"Evince",
"MuPDF",
"LibreOffice",
]
with mock.patch(
"subprocess.check_output", return_value=b"libreoffice-draw.desktop"
) as mock_default_mime_hander, mock.patch(
"os.listdir",
side_effect=[
["org.gnome.Evince.desktop"],
["org.pwmt.zathura-pdf-mupdf.desktop"],
["libreoffice-draw.desktop"],
],
) as mock_list, mock.patch(
"dangerzone.gui.logic.DesktopEntry", return_value=mock_desktop
):
dz = DangerzoneGui(mock_app, dummy)
mock_default_mime_hander.assert_called_once_with(
["xdg-mime", "query", "default", "application/pdf"]
)
mock_list.assert_called()
assert len(dz.pdf_viewers) == 3
assert dz.pdf_viewers.popitem(last=False)[0] == "LibreOffice"
@pytest.mark.skipif(platform.system() != "Linux", reason="Linux-only test")
def test_mime_handers_succeeds_no_default_found() -> None:
"""
Given a failure to return default mime handler,
ensure compatible applications are still returned.
"""
mock_app = mock.MagicMock()
dummy = mock.MagicMock()
mock_desktop = mock.MagicMock(spec=DesktopEntry)
mock_desktop.getMimeTypes.return_value = "application/pdf"
mock_desktop.getExec.side_effect = [
"/usr/bin/madeup-evince",
"/usr/local/bin/madeup-mupdf",
"/usr/local/bin/madeup-libredraw",
]
mock_desktop.getName.side_effect = [
"Evince",
"MuPDF",
"LibreOffice",
]
with mock.patch(
"subprocess.check_output",
side_effect=subprocess.CalledProcessError(1, "Oh no, xdg-mime error!)"),
) as mock_default_mime_hander, mock.patch(
"os.listdir",
side_effect=[
["org.gnome.Evince.desktop"],
["org.pwmt.zathura-pdf-mupdf.desktop"],
["libreoffice-draw.desktop"],
],
) as mock_list, mock.patch(
"dangerzone.gui.logic.DesktopEntry", return_value=mock_desktop
):
dz = DangerzoneGui(mock_app, dummy)
mock_default_mime_hander.assert_called_once_with(
["xdg-mime", "query", "default", "application/pdf"]
)
mock_list.assert_called()
assert len(dz.pdf_viewers) == 3
assert dz.pdf_viewers.popitem(last=False)[0] == "Evince"