summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/conftest.py31
-rw-r--r--tests/test_configuration.py142
-rw-r--r--tests/test_dll_detection.py129
-rw-r--r--tests/test_installation.py150
4 files changed, 452 insertions, 0 deletions
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..9ac31a0
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,31 @@
+"""
+Test configuration for the lsfg-vk plugin tests.
+"""
+
+import pytest
+from unittest.mock import Mock
+
+
+@pytest.fixture
+def mock_logger():
+ """Provide a mock logger for testing"""
+ return Mock()
+
+
+@pytest.fixture
+def mock_decky_logger(monkeypatch):
+ """Mock decky.logger for tests that import decky"""
+ mock_logger = Mock()
+
+ # Create a mock decky module
+ mock_decky = Mock()
+ mock_decky.logger = mock_logger
+
+ # Monkeypatch the import
+ monkeypatch.setattr('lsfg_vk.base_service.decky', mock_decky)
+ monkeypatch.setattr('lsfg_vk.installation.decky', mock_decky)
+ monkeypatch.setattr('lsfg_vk.dll_detection.decky', mock_decky)
+ monkeypatch.setattr('lsfg_vk.configuration.decky', mock_decky)
+ monkeypatch.setattr('lsfg_vk.plugin.decky', mock_decky)
+
+ return mock_logger
diff --git a/tests/test_configuration.py b/tests/test_configuration.py
new file mode 100644
index 0000000..a7f80b4
--- /dev/null
+++ b/tests/test_configuration.py
@@ -0,0 +1,142 @@
+"""
+Tests for the configuration service.
+"""
+
+import tempfile
+from pathlib import Path
+from unittest.mock import Mock
+
+from lsfg_vk.configuration import ConfigurationService
+
+
+def test_parse_script_content():
+ """Test parsing of script content"""
+ mock_logger = Mock()
+
+ with tempfile.TemporaryDirectory() as temp_dir:
+ temp_home = Path(temp_dir)
+
+ # Create service with mocked home directory
+ service = ConfigurationService(logger=mock_logger)
+ service.user_home = temp_home
+ service.lsfg_script_path = temp_home / "lsfg"
+
+ # Test script content
+ script_content = """#!/bin/bash
+
+export ENABLE_LSFG=1
+export LSFG_MULTIPLIER=3
+export LSFG_FLOW_SCALE=1.5
+export LSFG_HDR=1
+# export LSFG_PERF_MODE=1
+export MESA_VK_WSI_PRESENT_MODE=immediate # - disable vsync
+
+exec "$@"
+"""
+
+ config = service._parse_script_content(script_content)
+
+ assert config["enable_lsfg"] is True
+ assert config["multiplier"] == 3
+ assert config["flow_scale"] == 1.5
+ assert config["hdr"] is True
+ assert config["perf_mode"] is False # commented out
+ assert config["immediate_mode"] is True
+
+
+def test_parse_script_content_all_commented():
+ """Test parsing when all optional features are commented out"""
+ mock_logger = Mock()
+
+ with tempfile.TemporaryDirectory() as temp_dir:
+ temp_home = Path(temp_dir)
+
+ service = ConfigurationService(logger=mock_logger)
+ service.user_home = temp_home
+ service.lsfg_script_path = temp_home / "lsfg"
+
+ script_content = """#!/bin/bash
+
+# export ENABLE_LSFG=1
+export LSFG_MULTIPLIER=2
+export LSFG_FLOW_SCALE=1.0
+# export LSFG_HDR=1
+# export LSFG_PERF_MODE=1
+# export MESA_VK_WSI_PRESENT_MODE=immediate # - disable vsync
+
+exec "$@"
+"""
+
+ config = service._parse_script_content(script_content)
+
+ assert config["enable_lsfg"] is False
+ assert config["multiplier"] == 2
+ assert config["flow_scale"] == 1.0
+ assert config["hdr"] is False
+ assert config["perf_mode"] is False
+ assert config["immediate_mode"] is False
+
+
+def test_generate_script_content():
+ """Test script content generation"""
+ mock_logger = Mock()
+
+ with tempfile.TemporaryDirectory() as temp_dir:
+ temp_home = Path(temp_dir)
+
+ service = ConfigurationService(logger=mock_logger)
+ service.user_home = temp_home
+ service.lsfg_script_path = temp_home / "lsfg"
+
+ content = service._generate_script_content(
+ enable_lsfg=True,
+ multiplier=4,
+ flow_scale=2.0,
+ hdr=False,
+ perf_mode=True,
+ immediate_mode=False
+ )
+
+ assert "export ENABLE_LSFG=1" in content
+ assert "export LSFG_MULTIPLIER=4" in content
+ assert "export LSFG_FLOW_SCALE=2.0" in content
+ assert "# export LSFG_HDR=1" in content
+ assert "export LSFG_PERF_MODE=1" in content
+ assert "# export MESA_VK_WSI_PRESENT_MODE=immediate" in content
+
+
+def test_config_roundtrip():
+ """Test that we can write config and read it back correctly"""
+ mock_logger = Mock()
+
+ with tempfile.TemporaryDirectory() as temp_dir:
+ temp_home = Path(temp_dir)
+
+ service = ConfigurationService(logger=mock_logger)
+ service.user_home = temp_home
+ service.lsfg_script_path = temp_home / "lsfg"
+
+ # Update config
+ result = service.update_config(
+ enable_lsfg=True,
+ multiplier=3,
+ flow_scale=1.5,
+ hdr=True,
+ perf_mode=False,
+ immediate_mode=True
+ )
+
+ assert result["success"] is True
+
+ # Read it back
+ read_result = service.get_config()
+
+ assert read_result["success"] is True
+ config = read_result["config"]
+
+ assert config["enable_lsfg"] is True
+ assert config["multiplier"] == 3
+ assert config["flow_scale"] == 1.5
+ assert config["hdr"] is True
+ assert config["perf_mode"] is False
+ assert config["immediate_mode"] is True
diff --git a/tests/test_dll_detection.py b/tests/test_dll_detection.py
new file mode 100644
index 0000000..e50d733
--- /dev/null
+++ b/tests/test_dll_detection.py
@@ -0,0 +1,129 @@
+"""
+Tests for the DLL detection service.
+"""
+
+import os
+import tempfile
+from pathlib import Path
+from unittest.mock import Mock, patch
+
+from lsfg_vk.dll_detection import DllDetectionService
+from lsfg_vk.constants import LOSSLESS_DLL_NAME
+
+
+def test_dll_detection_via_env_variable():
+ """Test DLL detection via LSFG_DLL_PATH environment variable"""
+ mock_logger = Mock()
+
+ with tempfile.TemporaryDirectory() as temp_dir:
+ # Create a fake DLL file
+ dll_path = Path(temp_dir) / LOSSLESS_DLL_NAME
+ dll_path.write_text("fake dll content")
+
+ service = DllDetectionService(logger=mock_logger)
+
+ # Test with environment variable set
+ with patch.dict(os.environ, {"LSFG_DLL_PATH": str(dll_path)}):
+ result = service.check_lossless_scaling_dll()
+
+ assert result["detected"] is True
+ assert result["path"] == str(dll_path)
+ assert "LSFG_DLL_PATH" in result["source"]
+ assert result["error"] is None
+
+
+def test_dll_detection_via_xdg_data_home():
+ """Test DLL detection via XDG_DATA_HOME"""
+ mock_logger = Mock()
+
+ with tempfile.TemporaryDirectory() as temp_dir:
+ # Create the expected directory structure
+ steam_dir = Path(temp_dir) / "Steam" / "steamapps" / "common" / "Lossless Scaling"
+ steam_dir.mkdir(parents=True)
+
+ dll_path = steam_dir / LOSSLESS_DLL_NAME
+ dll_path.write_text("fake dll content")
+
+ service = DllDetectionService(logger=mock_logger)
+
+ # Test with XDG_DATA_HOME set, no LSFG_DLL_PATH
+ with patch.dict(os.environ, {"XDG_DATA_HOME": temp_dir}, clear=True):
+ result = service.check_lossless_scaling_dll()
+
+ assert result["detected"] is True
+ assert result["path"] == str(dll_path)
+ assert "XDG_DATA_HOME" in result["source"]
+ assert result["error"] is None
+
+
+def test_dll_detection_via_home_local_share():
+ """Test DLL detection via HOME/.local/share"""
+ mock_logger = Mock()
+
+ with tempfile.TemporaryDirectory() as temp_dir:
+ # Create the expected directory structure
+ steam_dir = Path(temp_dir) / ".local" / "share" / "Steam" / "steamapps" / "common" / "Lossless Scaling"
+ steam_dir.mkdir(parents=True)
+
+ dll_path = steam_dir / LOSSLESS_DLL_NAME
+ dll_path.write_text("fake dll content")
+
+ service = DllDetectionService(logger=mock_logger)
+
+ # Test with HOME set, no other env vars
+ env = {"HOME": temp_dir}
+ with patch.dict(os.environ, env, clear=True):
+ result = service.check_lossless_scaling_dll()
+
+ assert result["detected"] is True
+ assert result["path"] == str(dll_path)
+ assert "HOME/.local/share" in result["source"]
+ assert result["error"] is None
+
+
+def test_dll_detection_not_found():
+ """Test DLL detection when DLL is not found"""
+ mock_logger = Mock()
+
+ service = DllDetectionService(logger=mock_logger)
+
+ # Test with no environment variables set
+ with patch.dict(os.environ, {}, clear=True):
+ result = service.check_lossless_scaling_dll()
+
+ assert result["detected"] is False
+ assert result["path"] is None
+ assert result["source"] is None
+ assert "not found" in result["message"]
+ assert result["error"] is None
+
+
+def test_dll_detection_priority():
+ """Test that LSFG_DLL_PATH takes priority over other locations"""
+ mock_logger = Mock()
+
+ with tempfile.TemporaryDirectory() as temp_dir1, tempfile.TemporaryDirectory() as temp_dir2:
+ # Create DLL in both locations
+ dll_path1 = Path(temp_dir1) / LOSSLESS_DLL_NAME
+ dll_path1.write_text("fake dll content 1")
+
+ steam_dir = Path(temp_dir2) / "Steam" / "steamapps" / "common" / "Lossless Scaling"
+ steam_dir.mkdir(parents=True)
+ dll_path2 = steam_dir / LOSSLESS_DLL_NAME
+ dll_path2.write_text("fake dll content 2")
+
+ service = DllDetectionService(logger=mock_logger)
+
+ # Set both environment variables
+ env = {
+ "LSFG_DLL_PATH": str(dll_path1),
+ "XDG_DATA_HOME": temp_dir2
+ }
+
+ with patch.dict(os.environ, env, clear=True):
+ result = service.check_lossless_scaling_dll()
+
+ # Should prefer LSFG_DLL_PATH
+ assert result["detected"] is True
+ assert result["path"] == str(dll_path1)
+ assert "LSFG_DLL_PATH" in result["source"]
diff --git a/tests/test_installation.py b/tests/test_installation.py
new file mode 100644
index 0000000..2b3690e
--- /dev/null
+++ b/tests/test_installation.py
@@ -0,0 +1,150 @@
+"""
+Tests for the installation service.
+"""
+
+import os
+import tempfile
+import zipfile
+from pathlib import Path
+from unittest.mock import Mock, patch
+
+import pytest
+from pyfakefs.fake_filesystem_unittest import TestCase
+
+from lsfg_vk.installation import InstallationService
+from lsfg_vk.constants import LIB_FILENAME, JSON_FILENAME, ZIP_FILENAME
+
+
+class TestInstallationService(TestCase):
+ """Test cases for InstallationService using pyfakefs"""
+
+ def setUp(self):
+ """Set up fake filesystem"""
+ self.setUpPyfakefs()
+ self.mock_logger = Mock()
+
+ # Create a test home directory
+ self.test_home = Path("/home/testuser")
+ self.fs.create_dir(self.test_home)
+
+ # Patch Path.home() to return our test home
+ with patch('lsfg_vk.base_service.Path.home', return_value=self.test_home):
+ self.service = InstallationService(logger=self.mock_logger)
+
+ def test_check_installation_no_files(self):
+ """Test installation check when no files are installed"""
+ result = self.service.check_installation()
+
+ assert result["installed"] is False
+ assert result["lib_exists"] is False
+ assert result["json_exists"] is False
+ assert result["script_exists"] is False
+ assert result["error"] is None
+
+ def test_check_installation_all_files_exist(self):
+ """Test installation check when all files exist"""
+ # Create the files
+ self.service.lib_file.parent.mkdir(parents=True, exist_ok=True)
+ self.service.lib_file.touch()
+
+ self.service.json_file.parent.mkdir(parents=True, exist_ok=True)
+ self.service.json_file.touch()
+
+ self.service.lsfg_script_path.touch()
+
+ result = self.service.check_installation()
+
+ assert result["installed"] is True
+ assert result["lib_exists"] is True
+ assert result["json_exists"] is True
+ assert result["script_exists"] is True
+ assert result["error"] is None
+
+ def test_create_zip_for_testing(self):
+ """Helper to create a test zip file"""
+ # Create temp directory for zip contents
+ zip_content_dir = Path("/tmp/zip_content")
+ self.fs.create_dir(zip_content_dir)
+
+ # Create test files
+ lib_file = zip_content_dir / LIB_FILENAME
+ json_file = zip_content_dir / JSON_FILENAME
+
+ lib_file.write_text("fake library content")
+ json_file.write_text('{"layer": {"name": "VK_LAYER_LS_frame_generation"}}')
+
+ # Create zip file
+ zip_path = Path("/tmp/test.zip")
+ with zipfile.ZipFile(zip_path, 'w') as zip_file:
+ zip_file.write(lib_file, LIB_FILENAME)
+ zip_file.write(json_file, JSON_FILENAME)
+
+ return zip_path
+
+ @patch('lsfg_vk.installation.Path.home')
+ def test_install_success(self, mock_home):
+ """Test successful installation"""
+ mock_home.return_value = self.test_home
+
+ # Create the plugin directory and zip file
+ plugin_dir = Path("/plugin")
+ bin_dir = plugin_dir / "bin"
+ self.fs.create_dir(bin_dir)
+
+ # Create a test zip file
+ zip_path = self.test_create_zip_for_testing()
+ zip_dest = bin_dir / ZIP_FILENAME
+
+ # Copy our test zip to the expected location
+ with open(zip_path, 'rb') as src, open(zip_dest, 'wb') as dst:
+ dst.write(src.read())
+
+ # Mock the plugin directory detection
+ with patch('lsfg_vk.installation.Path.__file__', f"{plugin_dir}/lsfg_vk/installation.py"):
+ result = self.service.install()
+
+ assert result["success"] is True
+ assert "successfully" in result["message"]
+ assert result["error"] is None
+
+ # Check that files were created
+ assert self.service.lib_file.exists()
+ assert self.service.json_file.exists()
+ assert self.service.lsfg_script_path.exists()
+
+ def test_uninstall_no_files(self):
+ """Test uninstall when no files exist"""
+ result = self.service.uninstall()
+
+ assert result["success"] is True
+ assert "No lsfg-vk files found" in result["message"]
+ assert result["removed_files"] is None
+
+ def test_uninstall_with_files(self):
+ """Test uninstall when files exist"""
+ # Create the files
+ self.service.lib_file.parent.mkdir(parents=True, exist_ok=True)
+ self.service.lib_file.touch()
+
+ self.service.json_file.parent.mkdir(parents=True, exist_ok=True)
+ self.service.json_file.touch()
+
+ self.service.lsfg_script_path.touch()
+
+ result = self.service.uninstall()
+
+ assert result["success"] is True
+ assert "uninstalled successfully" in result["message"]
+ assert len(result["removed_files"]) == 3
+
+ # Check that files were removed
+ assert not self.service.lib_file.exists()
+ assert not self.service.json_file.exists()
+ assert not self.service.lsfg_script_path.exists()
+
+
+def test_installation_service_with_mock_logger():
+ """Test that InstallationService accepts a mock logger"""
+ mock_logger = Mock()
+ service = InstallationService(logger=mock_logger)
+ assert service.log == mock_logger