diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/conftest.py | 31 | ||||
| -rw-r--r-- | tests/test_configuration.py | 142 | ||||
| -rw-r--r-- | tests/test_dll_detection.py | 129 | ||||
| -rw-r--r-- | tests/test_installation.py | 150 |
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 |
