Skip to content

Commit

Permalink
Add tests for YAD GUI provider
Browse files Browse the repository at this point in the history
  • Loading branch information
Matoking committed Aug 5, 2021
1 parent 903d42f commit 229424f
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 65 deletions.
26 changes: 13 additions & 13 deletions tests/cli/test_launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ def home_cwd(home_dir, monkeypatch):
class TestCLIRun:
def test_run_executable(
self, steam_app_factory, default_proton,
command, zenity, launch_cli):
command, gui_provider, launch_cli):
"""
Run an EXE file by selecting using the GUI
"""
steam_app = steam_app_factory("Fake game", appid=10)

# Fake the user selecting the game
zenity.mock_stdout = "Fake game: 10"
gui_provider.mock_stdout = "Fake game: 10"

launch_cli(["test.exe"])

Expand All @@ -48,15 +48,15 @@ def test_run_executable_appid(
assert command.env["WINEPREFIX"] == str(steam_app.prefix_path)

def test_run_executable_no_selection(
self, default_proton, steam_app_factory, zenity,
self, default_proton, steam_app_factory, gui_provider,
launch_cli):
"""
Try running an EXE file but don't pick a Steam app
"""
steam_app_factory("Fake game", appid=10)

# Fake the user closing the form
zenity.mock_stdout = ""
gui_provider.mock_stdout = ""

result = launch_cli(["test.exe"], expect_exit=True)

Expand All @@ -72,17 +72,17 @@ def test_run_executable_no_apps(self, launch_cli):
assert "No Proton enabled Steam apps were found" in result

def test_run_executable_no_apps_from_desktop(
self, launch_cli, zenity):
self, launch_cli, gui_provider):
"""
Try running an EXE file when no Proton enabled Steam apps are installed
or ready, and ensure an error dialog is opened using `zenity`.
or ready, and ensure an error dialog is opened using `gui_provider`.
"""
launch_cli(["--no-term", "test.exe"], expect_exit=True)

assert zenity.args[0] == "zenity"
assert zenity.args[1] == "--text-info"
assert gui_provider.args[0] == "yad"
assert gui_provider.args[1] == "--text-info"

message = zenity.kwargs["input"]
message = gui_provider.kwargs["input"]

assert b"No Proton enabled Steam apps were found." in message

Expand Down Expand Up @@ -120,7 +120,7 @@ def test_run_executable_passthrough_arguments(

def test_cli_error_handler_uncaught_exception(
self, launch_cli, default_proton, steam_app_factory, monkeypatch,
zenity):
gui_provider):
"""
Ensure that 'cli_error_handler' correctly catches any uncaught
exception and includes a stack trace in the error dialog.
Expand All @@ -139,9 +139,9 @@ def _mock_from_appmanifest(*args, **kwargs):
["--no-term", "--appid", "10", "test.exe"], expect_exit=True
)

assert zenity.args[0] == "zenity"
assert zenity.args[1] == "--text-info"
assert gui_provider.args[0] == "yad"
assert gui_provider.args[1] == "--text-info"

message = zenity.kwargs["input"]
message = gui_provider.kwargs["input"]

assert b"Test appmanifest error" in message
31 changes: 17 additions & 14 deletions tests/cli/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,8 @@ def test_run_winetricks_not_found(
assert "Winetricks isn't installed" in result

def test_run_winetricks_from_desktop(
self, cli, default_proton, home_dir, steam_app_factory, zenity):
self, cli, default_proton, home_dir, steam_app_factory,
monkeypatch, gui_provider):
"""
Try performing a command with missing Winetricks executable.
Expand All @@ -325,27 +326,28 @@ def test_run_winetricks_from_desktop(

cli(["--no-term", "10", "winecfg"], expect_exit=True)

assert zenity.args[0] == "zenity"
assert zenity.args[1] == "--text-info"
assert gui_provider.args[0] == "yad"
assert gui_provider.args[1] == "--text-info"

message = zenity.kwargs["input"]
message = gui_provider.kwargs["input"]

assert b"Winetricks isn't installed" in message

# Also ensure log messages are included in the error message
assert b"Found Steam directory at" in message
assert b"Using default Steam Runtime" in message

def test_run_gui_zenity_not_found(self, cli, home_dir, steam_app_factory):
def test_run_gui_provider_not_found(self, cli, home_dir, steam_app_factory):
"""
Try performing a command with missing Zenity executable
Try performing a command with missing YAD or Zenity executable
"""
steam_app_factory(name="Fake game 1", appid=10)
(home_dir / ".local" / "bin" / "yad").unlink()
(home_dir / ".local" / "bin" / "zenity").unlink()

result = cli(["--gui"], expect_exit=True)

assert "Zenity is not installed" in result
assert "YAD or Zenity is not installed" in result

def test_run_steam_runtime_not_found(
self, cli, steam_dir, steam_app_factory):
Expand Down Expand Up @@ -456,7 +458,8 @@ def test_flatpak_detected(self, cli, monkeypatch, caplog):
in record.message

def test_cli_error_handler_uncaught_exception(
self, cli, default_proton, steam_app_factory, monkeypatch, zenity):
self, cli, default_proton, steam_app_factory, monkeypatch,
gui_provider):
"""
Ensure that 'cli_error_handler' correctly catches any uncaught
exception and includes a stack trace in the error dialog.
Expand All @@ -473,17 +476,17 @@ def _mock_from_appmanifest(*args, **kwargs):

cli(["--no-term", "-s", "Fake"], expect_exit=True)

assert zenity.args[0] == "zenity"
assert zenity.args[1] == "--text-info"
assert gui_provider.args[0] == "yad"
assert gui_provider.args[1] == "--text-info"

message = zenity.kwargs["input"]
message = gui_provider.kwargs["input"]

assert b"Test appmanifest error" in message


class TestCLIGUI:
def test_run_gui(
self, cli, default_proton, steam_app_factory, zenity, command,
self, cli, default_proton, steam_app_factory, gui_provider, command,
home_dir):
"""
Start the GUI and fake selecting a game
Expand All @@ -492,7 +495,7 @@ def test_run_gui(
proton_install_path = Path(default_proton.install_path)

# Fake the user selecting the game
zenity.mock_stdout = "Fake game 1: 10"
gui_provider.mock_stdout = "Fake game 1: 10"

cli(["--gui"])

Expand Down Expand Up @@ -528,7 +531,7 @@ def test_run_gui_no_games(self, cli, default_proton):

class TestCLICommand:
def test_run_command(
self, cli, default_proton, steam_app_factory, zenity, command,
self, cli, default_proton, steam_app_factory, gui_provider, command,
home_dir):
"""
Run a shell command for a given game
Expand Down
66 changes: 50 additions & 16 deletions tests/cli/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,67 @@
exit_with_error)


def test_cli_error_handler_uncaught_exception(
cli, default_proton, steam_app_factory, monkeypatch, zenity):
"""
Ensure that 'cli_error_handler' correctly catches any uncaught
exception and includes a stack trace in the error dialog.
"""
@pytest.fixture(scope="function")
def broken_appmanifest(monkeypatch):
def _mock_from_appmanifest(*args, **kwargs):
raise ValueError("Test appmanifest error")

steam_app_factory(name="Fake game", appid=10)

monkeypatch.setattr(
"protontricks.steam.SteamApp.from_appmanifest",
_mock_from_appmanifest
)


def test_cli_error_handler_uncaught_exception(
cli, default_proton, steam_app_factory, broken_appmanifest,
gui_provider):
"""
Ensure that 'cli_error_handler' correctly catches any uncaught
exception and includes a stack trace in the error dialog.
"""
steam_app_factory(name="Fake game", appid=10)

cli(["--no-term", "-s", "Fake"], expect_exit=True)

assert zenity.args[0] == "zenity"
assert zenity.args[1] == "--text-info"
assert gui_provider.args[0] == "yad"
assert gui_provider.args[1] == "--text-info"

message = gui_provider.kwargs["input"]

# 'broken_appmanifest' will induce an error in 'SteamApp.from_appmanifest'
assert b"Test appmanifest error" in message


@pytest.mark.parametrize("gui_cmd", ["yad", "zenity"])
def test_cli_error_handler_gui_provider_env(
cli, default_proton, steam_app_factory, monkeypatch,
broken_appmanifest, gui_provider, gui_cmd):
"""
Ensure that correct GUI provider is used depending on 'PROTONTRICKS_GUI'
environment variable
"""
monkeypatch.setenv("PROTONTRICKS_GUI", gui_cmd)

steam_app_factory(name="Fake game", appid=10)

message = zenity.kwargs["input"]
cli(["--no-term", "-s", "Fake"], expect_exit=True)

message = gui_provider.kwargs["input"]

assert b"Test appmanifest error" in message

if gui_cmd == "yad":
assert gui_provider.args[0] == "yad"
# YAD has custom button declarations
assert "--button=OK:1" in gui_provider.args
elif gui_cmd == "zenity":
assert gui_provider.args[0] == "zenity"
# Zenity doesn't have custom button declarations
assert "--button=OK:1" not in gui_provider.args



def test_exit_with_error_no_log_file(zenity):
def test_exit_with_error_no_log_file(gui_provider):
"""
Ensure that `exit_with_error` can show the error dialog even if
the log file goes missing for some reason
Expand All @@ -43,15 +77,15 @@ def test_exit_with_error_no_log_file(zenity):
with pytest.raises(SystemExit):
exit_with_error("Test error", desktop=True)

assert zenity.args[0] == "zenity"
assert zenity.args[1] == "--text-info"
assert gui_provider.args[0] == "yad"
assert gui_provider.args[1] == "--text-info"

message = zenity.kwargs["input"]
message = gui_provider.kwargs["input"]

assert b"Test error" in message


def test_log_file_cleanup(cli, steam_app_factory, zenity):
def test_log_file_cleanup(cli, steam_app_factory, gui_provider):
"""
Ensure that log file contains the log files generated during the
CLI call and that it is cleared after running `_delete_log_file`
Expand Down
30 changes: 22 additions & 8 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
cli as desktop_install_cli_entrypoint
from protontricks.cli.launch import cli as launch_cli_entrypoint
from protontricks.cli.main import cli as main_cli_entrypoint
from protontricks.gui import get_gui_provider
from protontricks.steam import (APPINFO_STRUCT_HEADER, APPINFO_STRUCT_SECTION,
SteamApp, get_appid_from_shortcut)

Expand All @@ -27,6 +28,16 @@ def env_vars(monkeypatch):
monkeypatch.setenv("STEAM_RUNTIME", "")


@pytest.fixture(scope="function", autouse=True)
def cleanup():
"""
Miscellaneous cleanup tasks that need to be done before each test
"""
# 'get_gui_provider' uses functools.lru_cache and needs to be cleared
# between tests
get_gui_provider.cache_clear()


@pytest.fixture(scope="function", autouse=True)
def home_dir(monkeypatch, tmp_path):
"""
Expand All @@ -40,10 +51,13 @@ def home_dir(monkeypatch, tmp_path):
(home_dir_ / ".local" / "bin" / "winetricks").touch()
(home_dir_ / ".local" / "bin" / "winetricks").chmod(0o744)

# Create fake Zenity executable
# Create fake YAD and Zenity executable
(home_dir_ / ".local" / "bin" / "zenity").touch()
(home_dir_ / ".local" / "bin" / "zenity").chmod(0o744)

(home_dir_ / ".local" / "bin" / "yad").touch()
(home_dir_ / ".local" / "bin" / "yad").chmod(0o744)

monkeypatch.setenv("HOME", str(home_dir_))

# Set PATH to point only towards the fake home directory
Expand Down Expand Up @@ -591,18 +605,18 @@ def __init__(self, stdout):


@pytest.fixture(scope="function")
def zenity(monkeypatch):
def gui_provider(monkeypatch):
"""
Monkeypatch the subprocess.run to store the args passed to the zenity
Monkeypatch the subprocess.run to store the args passed to the yad/zenity
command and to manipulate the output of the command
"""
mock_zenity = MockSubprocess()
mock_gui_provider = MockSubprocess()

def mock_subprocess_run(args, **kwargs):
mock_zenity.args = args
mock_zenity.kwargs = kwargs
mock_gui_provider.args = args
mock_gui_provider.kwargs = kwargs

return MockResult(stdout=mock_zenity.mock_stdout.encode("utf-8"))
return MockResult(stdout=mock_gui_provider.mock_stdout.encode("utf-8"))

monkeypatch.setattr(
"protontricks.gui.run",
Expand All @@ -613,7 +627,7 @@ def mock_subprocess_run(args, **kwargs):
mock_subprocess_run
)

yield mock_zenity
yield mock_gui_provider


@pytest.fixture(scope="function", autouse=True)
Expand Down

0 comments on commit 229424f

Please sign in to comment.