Merge pull request #30 from R1kaB3rN/download

Add download functionality to ulwgl_run
This commit is contained in:
Thomas Crider 2024-02-17 21:08:44 -07:00 committed by GitHub
commit b034600a9d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 689 additions and 17 deletions

258
ulwgl_dl_util.py Normal file
View File

@ -0,0 +1,258 @@
from pathlib import Path
from os import environ
from tarfile import open as tar_open
from typing import Dict, List, Tuple, Any, Union
from hashlib import sha512
from shutil import rmtree
from http.client import HTTPSConnection, HTTPResponse, HTTPException, HTTPConnection
from ssl import create_default_context
from json import loads as loads_json
from urllib.request import urlretrieve
def get_ulwgl_proton(env: Dict[str, str]) -> Union[Dict[str, str]]:
"""Attempt to find existing Proton from the system or downloads the latest if PROTONPATH is not set.
Only fetches the latest if not first found in .local/share/Steam/compatibilitytools.d
.cache/ULWGL is referenced for the latest then as fallback
"""
files: List[Tuple[str, str]] = []
try:
files = _fetch_releases()
except HTTPException:
print("Offline.\nContinuing ...")
cache: Path = Path.home().joinpath(".cache/ULWGL")
steam_compat: Path = Path.home().joinpath(".local/share/Steam/compatibilitytools.d")
cache.mkdir(exist_ok=True, parents=True)
steam_compat.mkdir(exist_ok=True, parents=True)
# Prioritize the Steam compat
if _get_from_steamcompat(env, steam_compat, cache, files):
return env
# Use the latest Proton in the cache if it exists
if _get_from_cache(env, steam_compat, cache, files, True):
return env
# Download the latest if Proton is not in Steam compat
# If the digests mismatched, refer to the cache in the next block
if _get_latest(env, steam_compat, cache, files):
return env
# Refer to an old version previously downloaded
# Reached on digest mismatch, user interrupt or download failure/no internet
if _get_from_cache(env, steam_compat, cache, files, False):
return env
# No internet and cache/compat tool is empty, just return and raise an exception from the caller
return env
def _fetch_releases() -> List[Tuple[str, str]]:
"""Fetch the latest releases from the Github API."""
files: List[Tuple[str, str]] = []
resp: HTTPResponse = None
conn: HTTPConnection = HTTPSConnection(
"api.github.com", timeout=30, context=create_default_context()
)
conn.request(
"GET",
"/repos/Open-Wine-Components/ULWGL-Proton/releases",
headers={
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
"User-Agent": "",
},
)
resp = conn.getresponse()
if resp and resp.status != 200:
return files
# Attempt to acquire the tarball and checksum from the JSON data
releases: List[Dict[str, Any]] = loads_json(resp.read().decode("utf-8"))
for release in releases:
if "assets" in release:
assets: List[Dict[str, Any]] = release["assets"]
for asset in assets:
if (
"name" in asset
and (
asset["name"].endswith("sum")
or (
asset["name"].endswith("tar.gz")
and asset["name"].startswith("ULWGL-Proton")
)
)
and "browser_download_url" in asset
):
if asset["name"].endswith("sum"):
files.append((asset["name"], asset["browser_download_url"]))
else:
files.append((asset["name"], asset["browser_download_url"]))
if len(files) == 2:
break
break
return files
def _fetch_proton(
env: Dict[str, str], steam_compat: Path, cache: Path, files: List[Tuple[str, str]]
) -> Dict[str, str]:
"""Download the latest ULWGL-Proton and set it as PROTONPATH."""
hash, hash_url = files[0]
proton, proton_url = files[1]
proton_dir: str = proton[: proton.find(".tar.gz")] # Proton dir
# TODO: Parallelize this
print(f"Downloading {hash} ...")
urlretrieve(hash_url, cache.joinpath(hash).as_posix())
print(f"Downloading {proton} ...")
urlretrieve(proton_url, cache.joinpath(proton).as_posix())
print("Completed.")
with cache.joinpath(proton).open(mode="rb") as file:
if (
sha512(file.read()).hexdigest()
!= cache.joinpath(hash).read_text().split(" ")[0]
):
err: str = "Digests mismatched.\nFalling back to cache ..."
raise ValueError(err)
print(f"{proton}: SHA512 is OK")
_extract_dir(cache.joinpath(proton), steam_compat)
environ["PROTONPATH"] = steam_compat.joinpath(proton_dir).as_posix()
env["PROTONPATH"] = environ["PROTONPATH"]
return env
def _extract_dir(proton: Path, steam_compat: Path) -> None:
"""Extract from the cache to another location."""
with tar_open(proton.as_posix(), "r:gz") as tar:
print(f"Extracting {proton} -> {steam_compat.as_posix()} ...")
tar.extractall(path=steam_compat.as_posix())
print("Completed.")
def _cleanup(tarball: str, proton: str, cache: Path, steam_compat: Path) -> None:
"""Remove files that may have been left in an incomplete state to avoid corruption.
We want to do this when a download for a new release is interrupted
"""
print("Keyboard Interrupt.\nCleaning ...")
if cache.joinpath(tarball).is_file():
print(f"Purging {tarball} in {cache} ...")
cache.joinpath(tarball).unlink()
if steam_compat.joinpath(proton).is_dir():
print(f"Purging {proton} in {steam_compat} ...")
rmtree(steam_compat.joinpath(proton).as_posix())
def _get_from_steamcompat(
env: Dict[str, str], steam_compat: Path, cache: Path, files: List[Tuple[str, str]]
) -> Union[Dict[str, str], None]:
"""Refer to Steam compat folder for any existing Proton directories."""
proton_dir: str = "" # Latest Proton
if len(files) == 2:
proton_dir: str = files[1][0][: files[1][0].find(".tar.gz")]
for proton in steam_compat.glob("ULWGL-Proton*"):
print(f"{proton.name} found in: {steam_compat.as_posix()}")
environ["PROTONPATH"] = proton.as_posix()
env["PROTONPATH"] = environ["PROTONPATH"]
# Notify the user that they're not using the latest
if proton_dir and proton.name != proton_dir:
print(
"ULWGL-Proton is outdated.\nFor latest release, please download "
+ files[1][1]
)
return env
return None
def _get_from_cache(
env: Dict[str, str],
steam_compat: Path,
cache: Path,
files: List[Tuple[str, str]],
use_latest=True,
) -> Union[Dict[str, str], None]:
"""Refer to ULWGL cache directory.
Use the latest in the cache when present. When download fails, use an old version
Older Proton versions are only referred to when: digests mismatch, user interrupt, or download failure/no internet
"""
path: Path = None
name: str = ""
for tarball in cache.glob("ULWGL-Proton*.tar.gz"):
if files and tarball == cache.joinpath(files[1][0]) and use_latest:
path = tarball
name = tarball.name
break
if tarball != cache.joinpath(files[1][0]) and not use_latest:
path = tarball
name = tarball.name
break
if path:
proton_dir: str = name[: name.find(".tar.gz")] # Proton dir
print(f"{name} found in: {path}")
try:
_extract_dir(path, steam_compat)
environ["PROTONPATH"] = steam_compat.joinpath(proton_dir).as_posix()
env["PROTONPATH"] = environ["PROTONPATH"]
return env
except KeyboardInterrupt:
if steam_compat.joinpath(proton_dir).is_dir():
print(f"Purging {proton_dir} in {steam_compat} ...")
rmtree(steam_compat.joinpath(proton_dir).as_posix())
raise
return None
def _get_latest(
env: Dict[str, str], steam_compat: Path, cache: Path, files: List[Tuple[str, str]]
) -> Union[Dict[str, str], None]:
"""Download the latest Proton for new installs -- empty cache and Steam compat.
When the digests mismatched or when interrupted, refer to cache for an old version
"""
if files:
print("Fetching latest release ...")
try:
_fetch_proton(env, steam_compat, cache, files)
env["PROTONPATH"] = environ["PROTONPATH"]
except ValueError:
# Digest mismatched or download failed
# Refer to the cache for old version next
return None
except KeyboardInterrupt:
tarball: str = files[1][0]
proton_dir: str = tarball[: tarball.find(".tar.gz")] # Proton dir
# Exit cleanly
# Clean up extracted data and cache to prevent corruption/errors
# Refer to the cache for old version next
_cleanup(tarball, proton_dir, cache, steam_compat)
return None
return env

View File

@ -10,6 +10,7 @@ from typing import Dict, Any, List, Set, Union, Tuple
import ulwgl_plugins
from re import match
import subprocess
from ulwgl_dl_util import get_ulwgl_proton
def parse_args() -> Union[Namespace, Tuple[str, List[str]]]: # noqa: D103
@ -118,9 +119,16 @@ def check_env(
"PROTONPATH" not in os.environ
or not Path(os.environ["PROTONPATH"]).expanduser().is_dir()
):
err: str = "Environment variable not set or not a directory: PROTONPATH"
raise ValueError(err)
env["PROTONPATH"] = os.environ["PROTONPATH"]
# Attempt to auto set this env var for the user
os.environ["PROTONPATH"] = ""
get_ulwgl_proton(env)
else:
env["PROTONPATH"] = os.environ["PROTONPATH"]
# If download fails/doesn't exist in the system, raise an error
if not os.environ["PROTONPATH"]:
err: str = "Download failed.\nProton could not be found in cache or compatibilitytools.d\nPlease set $PROTONPATH or visit https://github.com/Open-Wine-Components/ULWGL-Proton/releases"
raise FileNotFoundError(err)
return env

View File

@ -9,6 +9,8 @@ from tomllib import TOMLDecodeError
from shutil import rmtree
import re
import ulwgl_plugins
import ulwgl_dl_util
import tarfile
class TestGameLauncher(unittest.TestCase):
@ -49,6 +51,34 @@ class TestGameLauncher(unittest.TestCase):
self.test_file = "./tmp.WMYQiPb9A"
# Executable
self.test_exe = self.test_file + "/" + "foo"
# Cache
self.test_cache = Path("./tmp.5HYdpddgvs")
# Steam compat dir
self.test_compat = Path("./tmp.ZssGZoiNod")
# ULWGL-Proton dir
self.test_proton_dir = Path("ULWGL-Proton-5HYdpddgvs")
# ULWGL-Proton release
self.test_archive = Path(self.test_cache).joinpath(
f"{self.test_proton_dir}.tar.gz"
)
self.test_cache.mkdir(exist_ok=True)
self.test_compat.mkdir(exist_ok=True)
self.test_proton_dir.mkdir(exist_ok=True)
# Mock the proton file in the dir
self.test_proton_dir.joinpath("proton").touch(exist_ok=True)
# Mock the release downloaded in the cache: tmp.5HYdpddgvs/ULWGL-Proton-5HYdpddgvs.tar.gz
# Expected directory structure within the archive:
#
# +-- ULWGL-Proton-5HYdpddgvs (root directory)
# | +-- proton (normal file)
with tarfile.open(self.test_archive.as_posix(), "w:gz") as tar:
tar.add(
self.test_proton_dir.as_posix(), arcname=self.test_proton_dir.as_posix()
)
Path(self.test_file).mkdir(exist_ok=True)
Path(self.test_exe).touch()
@ -61,6 +91,365 @@ class TestGameLauncher(unittest.TestCase):
if Path(self.test_file).exists():
rmtree(self.test_file)
if self.test_cache.exists():
rmtree(self.test_cache.as_posix())
if self.test_compat.exists():
rmtree(self.test_compat.as_posix())
if self.test_proton_dir.exists():
rmtree(self.test_proton_dir.as_posix())
def test_latest_interrupt(self):
"""Test _get_latest in the event the user interrupts the download/extraction process.
Assumes a file is being downloaded or extracted in this case.
A KeyboardInterrupt should be raised, and the cache/compat dir should be cleaned afterwards.
"""
result = None
# In the real usage, should be populated after successful callout for latest Proton releases
# In this case, assume the test variable will be downloaded
files = [("", ""), (self.test_archive.name, "")]
# In the event of an interrupt, both the cache/compat dir will be checked for the latest release for removal
# We do this since the extraction process can be interrupted as well
ulwgl_dl_util._extract_dir(self.test_archive, self.test_compat)
with patch("ulwgl_dl_util._fetch_proton") as mock_function:
# Mock the interrupt
# We want the dir we tried to extract to be cleaned
mock_function.side_effect = KeyboardInterrupt
result = ulwgl_dl_util._get_latest(
self.env, self.test_compat, self.test_cache, files
)
self.assertFalse(self.env["PROTONPATH"], "Expected PROTONPATH to be empty")
self.assertFalse(result, "Expected None when a ValueError occurs")
# Verify the state of the compat dir/cache
self.assertFalse(
self.test_compat.joinpath(
self.test_archive.name[: self.test_archive.name.find(".tar.gz")]
).exists(),
"Expected Proton dir in compat to be cleaned",
)
self.assertFalse(
self.test_cache.joinpath(self.test_archive.name).exists(),
"Expected Proton dir in compat to be cleaned",
)
def test_latest_val_err(self):
"""Test _get_latest in the event something goes wrong in the download process for the latest Proton.
Assumes a file is being downloaded in this case.
A ValueError should be raised, and one case it can happen is if the digests mismatched for some reason
"""
result = None
# In the real usage, should be populated after successful callout for latest Proton releases
# When empty, it means the callout failed for some reason (e.g. no internet)
files = [("", ""), (self.test_archive.name, "")]
with patch("ulwgl_dl_util._fetch_proton") as mock_function:
# Mock the interrupt
mock_function.side_effect = ValueError
result = ulwgl_dl_util._get_latest(
self.env, self.test_compat, self.test_cache, files
)
self.assertFalse(self.env["PROTONPATH"], "Expected PROTONPATH to be empty")
self.assertFalse(result, "Expected None when a ValueError occurs")
def test_latest_offline(self):
"""Test _get_latest when the user doesn't have internet."""
result = None
# In the real usage, should be populated after successful callout for latest Proton releases
# When empty, it means the callout failed for some reason (e.g. no internet)
files = []
os.environ["PROTONPATH"] = ""
with patch("ulwgl_dl_util._fetch_proton"):
result = ulwgl_dl_util._get_latest(
self.env, self.test_compat, self.test_cache, files
)
self.assertFalse(self.env["PROTONPATH"], "Expected PROTONPATH to be empty")
self.assertTrue(result is self.env, "Expected the same reference")
def test_cache_interrupt(self):
"""Test _get_from_cache on keyboard interrupt on extraction from the cache to the compat dir."""
# In the real usage, should be populated after successful callout for latest Proton releases
# Just mock it and assumes its the latest
files = [("", ""), (self.test_archive.name, "")]
ulwgl_dl_util._extract_dir(self.test_archive, self.test_compat)
self.assertTrue(
self.test_compat.joinpath(
self.test_archive.name[: self.test_archive.name.find(".tar.gz")]
).exists(),
"Expected Proton dir to exist in compat",
)
with patch("ulwgl_dl_util._extract_dir") as mock_function:
with self.assertRaisesRegex(KeyboardInterrupt, ""):
# Mock the interrupt
# We want to simulate an interrupt mid-extraction in this case
# We want the dir we tried to extract to be cleaned
mock_function.side_effect = KeyboardInterrupt
ulwgl_dl_util._get_from_cache(
self.env, self.test_compat, self.test_cache, files, True
)
# After interrupt, we attempt to clean the compat dir for the file we tried to extract because it could be in an incomplete state
# Verify that the dir we tried to extract from cache is removed to avoid corruption on next launch
self.assertFalse(
self.test_compat.joinpath(
self.test_archive.name[: self.test_archive.name.find(".tar.gz")]
).exists(),
"Expected Proton dir in compat to be cleaned",
)
def test_cache_old(self):
"""Test _get_from_cache when the cache is empty.
In real usage, this only happens as a last resort when: download fails, digests mismatched, etc.
"""
result = None
# In the real usage, should be populated after successful callout for latest Proton releases
# Just mock it and assumes its the latest
files = [("", ""), (self.test_archive.name, "")]
# Mock old Proton versions in the cache
test_proton_dir = Path("ULWGL-Proton-foo")
test_proton_dir.mkdir(exist_ok=True)
test_archive = Path(self.test_cache).joinpath(
f"{test_proton_dir.as_posix()}.tar.gz"
)
with tarfile.open(test_archive.as_posix(), "w:gz") as tar:
tar.add(test_proton_dir.as_posix(), arcname=test_proton_dir.as_posix())
result = ulwgl_dl_util._get_from_cache(
self.env, self.test_compat, self.test_cache, files, False
)
# Verify that the old Proton was assigned
self.assertTrue(result is self.env, "Expected the same reference")
self.assertEqual(
self.env["PROTONPATH"],
self.test_compat.joinpath(
test_archive.name[: test_archive.name.find(".tar.gz")]
).as_posix(),
"Expected PROTONPATH to be proton dir in compat",
)
test_archive.unlink()
test_proton_dir.rmdir()
def test_cache_empty(self):
"""Test _get_from_cache when the cache is empty."""
result = None
# In the real usage, should be populated after successful callout for latest Proton releases
# Just mock it and assumes its the latest
files = [("", ""), (self.test_archive.name, "")]
self.test_archive.unlink()
result = ulwgl_dl_util._get_from_cache(
self.env, self.test_compat, self.test_cache, files, True
)
self.assertFalse(result, "Expected None when calling _get_from_cache")
self.assertFalse(
self.env["PROTONPATH"],
"Expected PROTONPATH to be empty when the cache is empty",
)
def test_cache(self):
"""Test _get_from_cache.
Tests the case when the latest Proton already exists in the cache
"""
result = None
# In the real usage, should be populated after successful callout for latest Proton releases
# Just mock it and assumes its the latest
files = [("", ""), (self.test_archive.name, "")]
result = ulwgl_dl_util._get_from_cache(
self.env, self.test_compat, self.test_cache, files, True
)
self.assertTrue(result is self.env, "Expected the same reference")
self.assertEqual(
self.env["PROTONPATH"],
self.test_compat.joinpath(
self.test_archive.name[: self.test_archive.name.find(".tar.gz")]
).as_posix(),
"Expected PROTONPATH to be proton dir in compat",
)
def test_steamcompat_nodir(self):
"""Test _get_from_steamcompat when a Proton doesn't exist in the Steam compat dir.
In this case, the None should be returned to signal that we should continue with downloading the latest Proton
"""
result = None
files = [("", ""), (self.test_archive.name, "")]
result = ulwgl_dl_util._get_from_steamcompat(
self.env, self.test_compat, self.test_cache, files
)
self.assertFalse(result, "Expected None after calling _get_from_steamcompat")
self.assertFalse(self.env["PROTONPATH"], "Expected PROTONPATH to not be set")
def test_steamcompat(self):
"""Test _get_from_steamcompat.
When a Proton exist in .local/share/Steam/compatibilitytools.d, use it when PROTONPATH is unset
"""
result = None
files = [("", ""), (self.test_archive.name, "")]
ulwgl_dl_util._extract_dir(self.test_archive, self.test_compat)
result = ulwgl_dl_util._get_from_steamcompat(
self.env, self.test_compat, self.test_cache, files
)
self.assertTrue(result is self.env, "Expected the same reference")
self.assertEqual(
self.env["PROTONPATH"],
self.test_compat.joinpath(
self.test_archive.name[: self.test_archive.name.find(".tar.gz")]
).as_posix(),
"Expected PROTONPATH to be proton dir in compat",
)
def test_cleanup_no_exists(self):
"""Test _cleanup when passed files that do not exist.
In the event of an interrupt during the download/extract process, we only want to clean the files that exist
NOTE: This is **extremely** important, as we do **not** want to delete anything else but the files we downloaded/extracted -- the incomplete tarball/extracted dir
"""
result = None
ulwgl_dl_util._extract_dir(self.test_archive, self.test_compat)
# Create a file in the cache and compat
self.test_cache.joinpath("foo").touch()
self.test_compat.joinpath("foo").touch()
# Before cleaning
# On setUp, an archive is created and a dir should exist in compat after extraction
self.assertTrue(
self.test_compat.joinpath("foo").exists(),
"Expected test file to exist in compat before cleaning",
)
self.assertTrue(
self.test_cache.joinpath("foo").exists(),
"Expected test file to exist in cache before cleaning",
)
self.assertTrue(
self.test_archive.exists(),
"Expected archive to exist in cache before cleaning",
)
self.assertTrue(
self.test_compat.joinpath(self.test_proton_dir).joinpath("proton").exists(),
"Expected 'proton' to exist before cleaning",
)
# Pass files that do not exist
result = ulwgl_dl_util._cleanup(
"foo.tar.gz",
"foo",
self.test_cache,
self.test_compat,
)
# Verify state of cache and compat after cleaning
self.assertFalse(result, "Expected None after cleaning")
self.assertTrue(
self.test_compat.joinpath("foo").exists(),
"Expected test file to exist in compat after cleaning",
)
self.assertTrue(
self.test_cache.joinpath("foo").exists(),
"Expected test file to exist in cache after cleaning",
)
self.assertTrue(
self.test_compat.joinpath(self.test_proton_dir).exists(),
"Expected proton dir to still exist after cleaning",
)
self.assertTrue(
self.test_archive.exists(),
"Expected archive to still exist after cleaning",
)
self.assertTrue(
self.test_compat.joinpath(self.test_proton_dir).joinpath("proton").exists(),
"Expected 'proton' to still exist after cleaning",
)
def test_cleanup(self):
"""Test _cleanup.
In the event of an interrupt during the download/extract process, we want to clean the cache or the extracted dir in Steam compat to avoid incomplete files
"""
result = None
ulwgl_dl_util._extract_dir(self.test_archive, self.test_compat)
result = ulwgl_dl_util._cleanup(
self.test_proton_dir.as_posix() + ".tar.gz",
self.test_proton_dir.as_posix(),
self.test_cache,
self.test_compat,
)
self.assertFalse(result, "Expected None after cleaning")
self.assertFalse(
self.test_compat.joinpath(self.test_proton_dir).exists(),
"Expected proton dir to be cleaned in compat",
)
self.assertFalse(
self.test_archive.exists(),
"Expected archive to be cleaned in cache",
)
self.assertFalse(
self.test_compat.joinpath(self.test_proton_dir).joinpath("proton").exists(),
"Expected 'proton' to not exist after cleaned",
)
def test_extract_err(self):
"""Test _extract_dir when passed a non-gzip compressed archive.
An error should be raised as we only expect .tar.gz releases
"""
test_archive = self.test_cache.joinpath(f"{self.test_proton_dir}.tar")
# Do not apply compression
with tarfile.open(test_archive.as_posix(), "w") as tar:
tar.add(
self.test_proton_dir.as_posix(), arcname=self.test_proton_dir.as_posix()
)
with self.assertRaisesRegex(tarfile.ReadError, "gzip"):
ulwgl_dl_util._extract_dir(test_archive, self.test_compat)
if test_archive.exists():
test_archive.unlink()
def test_extract(self):
"""Test _extract_dir.
An error should not be raised when the Proton release is extracted to the Steam compat dir
"""
result = None
result = ulwgl_dl_util._extract_dir(self.test_archive, self.test_compat)
self.assertFalse(result, "Expected None after extracting")
self.assertTrue(
self.test_compat.joinpath(self.test_proton_dir).exists(),
"Expected proton dir to exists in compat",
)
self.assertTrue(
self.test_compat.joinpath(self.test_proton_dir).joinpath("proton").exists(),
"Expected 'proton' file to exists in the proton dir",
)
def test_game_drive_empty(self):
"""Test enable_steam_game_drive.
@ -1053,20 +1442,27 @@ class TestGameLauncher(unittest.TestCase):
result, Namespace, "Expected a Namespace from parse_arg"
)
def test_env_proton_dir(self):
"""Test check_env when $PROTONPATH is not a directory.
def test_env_proton_nodir(self):
"""Test check_env when $PROTONPATH is not set on failing to setting it.
An ValueError should occur if the value is not a directory
An FileNotFoundError should be raised when we fail to set PROTONPATH
"""
with self.assertRaisesRegex(ValueError, "PROTONPATH"):
os.environ["WINEPREFIX"] = self.test_file
os.environ["GAMEID"] = self.test_file
os.environ["PROTONPATH"] = "./foo"
ulwgl_run.check_env(self.env)
self.assertFalse(
Path(os.environ["PROTONPATH"]).is_dir(),
"Expected PROTONPATH to not be a directory",
)
result = None
# Mock getting the Proton
with self.assertRaises(FileNotFoundError):
with patch.object(
ulwgl_run,
"get_ulwgl_proton",
return_value=self.env,
):
os.environ["WINEPREFIX"] = self.test_file
os.environ["GAMEID"] = self.test_file
result = ulwgl_run.check_env(self.env)
# Mock setting it on success
os.environ["PROTONPATH"] = self.test_file
self.assertTrue(result is self.env, "Expected the same reference")
self.assertFalse(os.environ["PROTONPATH"])
def test_env_wine_dir(self):
"""Test check_env when $WINEPREFIX is not a directory.
@ -1135,10 +1531,20 @@ class TestGameLauncher(unittest.TestCase):
def test_env_vars_proton(self):
"""Test check_env when setting only $WINEPREFIX and $GAMEID."""
with self.assertRaisesRegex(ValueError, "PROTONPATH"):
with self.assertRaisesRegex(FileNotFoundError, "Proton"):
os.environ["WINEPREFIX"] = self.test_file
os.environ["GAMEID"] = self.test_file
ulwgl_run.check_env(self.env)
# Mock getting the Proton
with patch.object(
ulwgl_run,
"get_ulwgl_proton",
return_value=self.env,
):
os.environ["WINEPREFIX"] = self.test_file
os.environ["GAMEID"] = self.test_file
result = ulwgl_run.check_env(self.env)
self.assertTrue(result is self.env, "Expected the same reference")
self.assertFalse(os.environ["PROTONPATH"])
def test_env_vars_wine(self):
"""Test check_env when setting only $WINEPREFIX."""