steam-client 2.1.0.dev0__tar.gz → 3.0.0.dev0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. {steam-client-2.1.0.dev0/steam_client.egg-info → steam_client-3.0.0.dev0}/PKG-INFO +5 -2
  2. {steam-client-2.1.0.dev0 → steam_client-3.0.0.dev0}/pyproject.toml +4 -4
  3. {steam-client-2.1.0.dev0 → steam_client-3.0.0.dev0}/steam_client/app.py +2 -2
  4. {steam-client-2.1.0.dev0 → steam_client-3.0.0.dev0}/steam_client/commands.py +10 -10
  5. {steam-client-2.1.0.dev0 → steam_client-3.0.0.dev0}/steam_client/game.py +29 -12
  6. {steam-client-2.1.0.dev0 → steam_client-3.0.0.dev0}/steam_client/library.py +2 -3
  7. {steam-client-2.1.0.dev0 → steam_client-3.0.0.dev0}/steam_client/library_directory.py +0 -3
  8. {steam-client-2.1.0.dev0 → steam_client-3.0.0.dev0}/steam_client/utils.py +7 -3
  9. {steam-client-2.1.0.dev0 → steam_client-3.0.0.dev0/steam_client.egg-info}/PKG-INFO +5 -2
  10. {steam-client-2.1.0.dev0 → steam_client-3.0.0.dev0}/steam_client.egg-info/SOURCES.txt +6 -1
  11. steam_client-3.0.0.dev0/tests/test_commands.py +61 -0
  12. steam_client-3.0.0.dev0/tests/test_game.py +61 -0
  13. steam_client-3.0.0.dev0/tests/test_login_users.py +112 -0
  14. steam_client-3.0.0.dev0/tests/test_shortcut.py +69 -0
  15. steam_client-3.0.0.dev0/tests/test_steam.py +34 -0
  16. {steam-client-2.1.0.dev0 → steam_client-3.0.0.dev0}/LICENSE +0 -0
  17. {steam-client-2.1.0.dev0 → steam_client-3.0.0.dev0}/setup.cfg +0 -0
  18. {steam-client-2.1.0.dev0 → steam_client-3.0.0.dev0}/setup.py +0 -0
  19. {steam-client-2.1.0.dev0 → steam_client-3.0.0.dev0}/steam_client/__init__.py +0 -0
  20. {steam-client-2.1.0.dev0 → steam_client-3.0.0.dev0}/steam_client/login_users.py +0 -0
  21. {steam-client-2.1.0.dev0 → steam_client-3.0.0.dev0}/steam_client/py.typed +0 -0
  22. {steam-client-2.1.0.dev0 → steam_client-3.0.0.dev0}/steam_client/shortcut.py +0 -0
  23. {steam-client-2.1.0.dev0 → steam_client-3.0.0.dev0}/steam_client/steam.py +0 -0
  24. {steam-client-2.1.0.dev0 → steam_client-3.0.0.dev0}/steam_client/web_api.py +0 -0
  25. {steam-client-2.1.0.dev0 → steam_client-3.0.0.dev0}/steam_client.egg-info/dependency_links.txt +0 -0
  26. {steam-client-2.1.0.dev0 → steam_client-3.0.0.dev0}/steam_client.egg-info/requires.txt +0 -0
  27. {steam-client-2.1.0.dev0 → steam_client-3.0.0.dev0}/steam_client.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: steam-client
3
- Version: 2.1.0.dev0
3
+ Version: 3.0.0.dev0
4
4
  Summary: Steam client library for Python
5
5
  Author-email: William McAllister <dev.garulf@gmail.com>
6
6
  License: MIT
@@ -11,3 +11,6 @@ Classifier: Programming Language :: Python :: 3 :: Only
11
11
  Requires-Python: >=3.8
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
+ Requires-Dist: vdf>=3.4
15
+ Requires-Dist: pycrc>=0.10.0
16
+ Dynamic: license-file
@@ -6,7 +6,7 @@ name = "steam-client"
6
6
  authors = [
7
7
  {name = "William McAllister", email = "dev.garulf@gmail.com"}
8
8
  ]
9
- version = "2.1.0-dev.0"
9
+ version = "3.0.0-dev.0"
10
10
  description = "Steam client library for Python"
11
11
  readme = "README.md"
12
12
  requires-python = ">=3.8"
@@ -31,7 +31,9 @@ packages = ["steam_client"]
31
31
  "steam_client" = ["py.typed"]
32
32
 
33
33
  [tool.bumpversion]
34
- current_version = "2.0.1-dev.0"
34
+ current_version = "3.0.0-dev.0"
35
+ search = "version = \"{current_version}\""
36
+ replace = "version = \"{new_version}\""
35
37
  parse = """(?x)
36
38
  (?P<major>[0-9]+)
37
39
  \\.(?P<minor>[0-9]+)
@@ -45,8 +47,6 @@ serialize = [
45
47
  "{major}.{minor}.{patch}-{label}.{label_n}",
46
48
  "{major}.{minor}.{patch}",
47
49
  ]
48
- search = "version = '{current_version}'"
49
- replace = "version = '{new_version}'"
50
50
  tag = true
51
51
  allow_dirty = false
52
52
  commit = true
@@ -4,7 +4,7 @@ from pathlib import Path
4
4
  from typing import TYPE_CHECKING
5
5
 
6
6
 
7
- from .commands import Commands
7
+ from .commands import Commands
8
8
 
9
9
  if TYPE_CHECKING:
10
10
  from .steam import Steam
@@ -31,7 +31,7 @@ class App(ABC):
31
31
 
32
32
  @property
33
33
  @abstractmethod
34
- def icon(self) -> Path:
34
+ def icon(self) -> Path | None:
35
35
  """Returns the path to the icon image."""
36
36
  pass
37
37
 
@@ -4,6 +4,7 @@ from enum import StrEnum
4
4
 
5
5
  SCHEME = 'steam'
6
6
 
7
+
7
8
  class SteamWindows(StrEnum):
8
9
  """Enumeration of Steam client windows."""
9
10
  MAIN = 'main'
@@ -19,8 +20,9 @@ class SteamWindows(StrEnum):
19
20
  TOOLS = 'tools'
20
21
  CONSOLE = 'console'
21
22
 
23
+
22
24
  class Command():
23
-
25
+
24
26
  def __init__(self, scheme: str):
25
27
  self._scheme = scheme
26
28
 
@@ -38,32 +40,30 @@ class Commands:
38
40
  def __init__(self):
39
41
  self._command = Command(SCHEME)
40
42
 
41
- def run_game_id(self, app_id: str) -> str:
43
+ def run_game_id(self, app_id: str) -> None:
42
44
  """Launches game with the specified ID in the Steam client."""
43
45
  self._command(['rungameid'], app_id)
44
46
 
45
- def store(self, app_id: str) -> str:
47
+ def store(self, app_id: str) -> None:
46
48
  """Opens the game's store page in the Steam client."""
47
49
  self._command(['store'], app_id)
48
50
 
49
- def install(self, app_id: str) -> str:
51
+ def install(self, app_id: str) -> None:
50
52
  """Opens the game's install prompt in the Steam client."""
51
53
  self._command(['install'], app_id)
52
54
 
53
- def uninstall(self, app_id: str) -> str:
55
+ def uninstall(self, app_id: str) -> None:
54
56
  """Opens the game's uninstall prompt in the Steam client."""
55
57
  self._command(['uninstall'], app_id)
56
58
 
57
- def update_news(self, app_id: str) -> str:
59
+ def update_news(self, app_id: str) -> None:
58
60
  """Opens the game's update news in the Steam client."""
59
61
  self._command(['updatenews'], app_id)
60
62
 
61
- def open(self, window: SteamWindows) -> str:
63
+ def open(self, window: SteamWindows) -> None:
62
64
  """Opens the specified window in the Steam client."""
63
65
  self._command(['open'], window)
64
66
 
65
- def open_url(self, url: str) -> str:
67
+ def open_url(self, url: str) -> None:
66
68
  """Opens the specified URL in the Steam client."""
67
69
  self._command(['openurl'], url)
68
-
69
-
@@ -3,7 +3,7 @@ import functools
3
3
  from pathlib import Path
4
4
  from typing import TYPE_CHECKING
5
5
 
6
- import vdf
6
+ import vdf # type: ignore
7
7
 
8
8
  if TYPE_CHECKING:
9
9
  from .steam import Steam
@@ -13,13 +13,13 @@ from .app import App
13
13
 
14
14
  UNKNOWN_GAME_NAME = 'UNKNOWN'
15
15
 
16
- ASSETS = [
16
+ ASSETS = frozenset({
17
17
  "header.jpg",
18
18
  "library_600x900.jpg",
19
19
  "library_hero.jpg",
20
20
  "library_hero_blur.jpg",
21
21
  "logo.png"
22
- ]
22
+ })
23
23
 
24
24
 
25
25
  class SteamGame(App):
@@ -28,10 +28,16 @@ class SteamGame(App):
28
28
  def __init__(self, steam: Steam, library_path: str, appid: str):
29
29
  self.library_path = library_path
30
30
  self._appid = appid
31
+ self._icon: Path | None = None
32
+ self._name: str | None = None
31
33
  super().__init__(steam)
32
34
 
33
35
  def __repr__(self) -> str:
34
- return f'Game(steam={self._steam.__repr__()}, library_path={self.library_path.__repr__()}, appid={self.appid.__repr__()})'
36
+ return (
37
+ f'Game(steam={self._steam.__repr__()}, '
38
+ f'library_path={self.library_path.__repr__()}, '
39
+ f'appid={self.appid.__repr__()})'
40
+ )
35
41
 
36
42
  @property
37
43
  def asset_dir(self) -> Path:
@@ -39,12 +45,18 @@ class SteamGame(App):
39
45
  return Path(self._steam.library_cache).joinpath(self.appid)
40
46
 
41
47
  @property
42
- def icon(self) -> Path:
48
+ def icon(self) -> Path | None:
43
49
  """Returns the path to the icon image."""
44
- for asset in self.asset_dir.iterdir():
45
- if asset.name not in ASSETS:
46
- return asset
47
- return Path(self._steam.library_cache).joinpath(f'{self.appid}_icon.jpg')
50
+ if self._icon is None:
51
+ self._icon = next(
52
+ (
53
+ asset
54
+ for asset in self.asset_dir.iterdir()
55
+ if asset.is_file() and asset.name not in ASSETS
56
+ ),
57
+ None,
58
+ )
59
+ return self._icon
48
60
 
49
61
  @property
50
62
  def header(self) -> Path:
@@ -81,9 +93,14 @@ class SteamGame(App):
81
93
  except FileNotFoundError:
82
94
  return {}
83
95
 
84
- @functools.cached_property
96
+ @property
85
97
  def name(self) -> str:
86
98
  """Returns the name of the game."""
99
+ if self._name is None:
100
+ self._name = self._get_name_from_manifest()
101
+ return self._name
102
+
103
+ def _get_name_from_manifest(self) -> str:
87
104
  try:
88
105
  return self._manifest['AppState']['name']
89
106
  except KeyError:
@@ -96,8 +113,8 @@ class SteamGame(App):
96
113
 
97
114
  def open_store_page(self):
98
115
  """Opens the game's store page in the Steam client."""
99
- self._steam.commands.store(self.appid)
116
+ self._commands.store(self.appid)
100
117
 
101
118
  def uninstall(self):
102
119
  """Uninstalls the game."""
103
- self._steam.commands.uninstall(self.appid)
120
+ self._commands.uninstall(self.appid)
@@ -1,9 +1,8 @@
1
1
  from __future__ import annotations
2
- from pathlib import Path
3
2
  import hashlib
4
3
  from typing import TYPE_CHECKING, List, Optional, Union
5
4
 
6
- import vdf
5
+ import vdf # type: ignore
7
6
 
8
7
  from steam_client.shortcut import Shortcut
9
8
 
@@ -70,7 +69,7 @@ class Library:
70
69
  def shortcuts(self) -> List[Shortcut]:
71
70
  """Returns the Non-Steam shortcuts from the Steam library."""
72
71
  shortcuts = []
73
- for user in self._steam.login_users.users():
72
+ for user in self._steam.users:
74
73
  shortcuts.extend(user.shortcuts())
75
74
  return shortcuts
76
75
 
@@ -1,9 +1,6 @@
1
1
  from __future__ import annotations
2
2
  from dataclasses import dataclass
3
3
  from typing import TYPE_CHECKING, List
4
- from pathlib import Path
5
-
6
- import vdf
7
4
 
8
5
  if TYPE_CHECKING:
9
6
  from .steam import Steam
@@ -1,5 +1,5 @@
1
- import winreg as reg
2
- from winreg import HKEY_LOCAL_MACHINE
1
+ import importlib
2
+ import sys
3
3
 
4
4
  from .steam import Steam
5
5
 
@@ -9,7 +9,11 @@ STEAM_SUB_KEY = r'SOFTWARE\WOW6432Node\Valve\Steam'
9
9
 
10
10
  def get_steam_from_registry() -> str:
11
11
  """Returns the Steam install path from the Windows registry."""
12
- with reg.OpenKey(HKEY_LOCAL_MACHINE, STEAM_SUB_KEY) as hkey:
12
+ if sys.platform != "win32":
13
+ raise OSError("Windows registry is only available on Windows")
14
+
15
+ reg = importlib.import_module("winreg")
16
+ with reg.OpenKey(reg.HKEY_LOCAL_MACHINE, STEAM_SUB_KEY) as hkey:
13
17
  return reg.QueryValueEx(hkey, "InstallPath")[0]
14
18
 
15
19
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: steam-client
3
- Version: 2.1.0.dev0
3
+ Version: 3.0.0.dev0
4
4
  Summary: Steam client library for Python
5
5
  Author-email: William McAllister <dev.garulf@gmail.com>
6
6
  License: MIT
@@ -11,3 +11,6 @@ Classifier: Programming Language :: Python :: 3 :: Only
11
11
  Requires-Python: >=3.8
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
+ Requires-Dist: vdf>=3.4
15
+ Requires-Dist: pycrc>=0.10.0
16
+ Dynamic: license-file
@@ -17,4 +17,9 @@ steam_client.egg-info/PKG-INFO
17
17
  steam_client.egg-info/SOURCES.txt
18
18
  steam_client.egg-info/dependency_links.txt
19
19
  steam_client.egg-info/requires.txt
20
- steam_client.egg-info/top_level.txt
20
+ steam_client.egg-info/top_level.txt
21
+ tests/test_commands.py
22
+ tests/test_game.py
23
+ tests/test_login_users.py
24
+ tests/test_shortcut.py
25
+ tests/test_steam.py
@@ -0,0 +1,61 @@
1
+ from unittest.mock import patch
2
+
3
+ from steam_client.commands import Command, Commands, SteamWindows, SCHEME
4
+
5
+
6
+ class TestCommand:
7
+ def setup_method(self):
8
+ self.command = Command(SCHEME)
9
+
10
+ def test_create_uri_single_path(self):
11
+ uri = self.command._create_uri(["rungameid"], "12345")
12
+ assert uri == "steam://rungameid/12345"
13
+
14
+ def test_create_uri_multi_path(self):
15
+ uri = self.command._create_uri(["store", "app"], "67890")
16
+ assert uri == "steam://store/app/67890"
17
+
18
+ def test_call_opens_browser(self):
19
+ with patch("webbrowser.open") as mock_open:
20
+ self.command(["rungameid"], "12345")
21
+ mock_open.assert_called_once_with("steam://rungameid/12345")
22
+
23
+
24
+ class TestCommands:
25
+ def setup_method(self):
26
+ self.commands = Commands()
27
+
28
+ def test_run_game_id(self):
29
+ with patch("webbrowser.open") as mock_open:
30
+ self.commands.run_game_id("12345")
31
+ mock_open.assert_called_once_with("steam://rungameid/12345")
32
+
33
+ def test_store(self):
34
+ with patch("webbrowser.open") as mock_open:
35
+ self.commands.store("12345")
36
+ mock_open.assert_called_once_with("steam://store/12345")
37
+
38
+ def test_install(self):
39
+ with patch("webbrowser.open") as mock_open:
40
+ self.commands.install("12345")
41
+ mock_open.assert_called_once_with("steam://install/12345")
42
+
43
+ def test_uninstall(self):
44
+ with patch("webbrowser.open") as mock_open:
45
+ self.commands.uninstall("12345")
46
+ mock_open.assert_called_once_with("steam://uninstall/12345")
47
+
48
+ def test_update_news(self):
49
+ with patch("webbrowser.open") as mock_open:
50
+ self.commands.update_news("12345")
51
+ mock_open.assert_called_once_with("steam://updatenews/12345")
52
+
53
+ def test_open_window(self):
54
+ with patch("webbrowser.open") as mock_open:
55
+ self.commands.open(SteamWindows.SETTINGS)
56
+ mock_open.assert_called_once_with(f"steam://open/{SteamWindows.SETTINGS}")
57
+
58
+ def test_open_url(self):
59
+ with patch("webbrowser.open") as mock_open:
60
+ self.commands.open_url("https://store.steampowered.com")
61
+ mock_open.assert_called_once_with("steam://openurl/https://store.steampowered.com")
@@ -0,0 +1,61 @@
1
+ from pathlib import Path
2
+ import pytest
3
+
4
+ from steam_client.game import SteamGame, UNKNOWN_GAME_NAME
5
+
6
+ LIBRARY_PATH = "/library"
7
+ APPID = "12345"
8
+
9
+
10
+ @pytest.fixture
11
+ def game(steam):
12
+ return SteamGame(steam, LIBRARY_PATH, APPID)
13
+
14
+
15
+ def test_game_appid(game):
16
+ assert game.appid == APPID
17
+
18
+
19
+ def test_game_repr(game, steam):
20
+ r = repr(game)
21
+ assert APPID in r
22
+ assert LIBRARY_PATH in r
23
+
24
+
25
+ def test_manifest_path(game):
26
+ assert game.manifest_path == Path(LIBRARY_PATH) / "steamapps" / f"appmanifest_{APPID}.acf"
27
+
28
+
29
+ def test_asset_dir(game):
30
+ assert game.asset_dir == Path("/fake/steam/appcache/librarycache") / APPID
31
+
32
+
33
+ def test_header(game):
34
+ assert game.header == Path("/fake/steam/appcache/librarycache") / f"{APPID}_header.jpg"
35
+
36
+
37
+ def test_grid(game):
38
+ assert game.grid == Path("/fake/steam/appcache/librarycache") / f"{APPID}_library_600x900.jpg"
39
+
40
+
41
+ def test_hero(game):
42
+ assert game.hero == Path("/fake/steam/appcache/librarycache") / f"{APPID}_library_hero.jpg"
43
+
44
+
45
+ def test_hero_blur(game):
46
+ assert game.hero_blur == Path("/fake/steam/appcache/librarycache") / f"{APPID}_library_hero_blur.jpg"
47
+
48
+
49
+ def test_game_name_from_manifest(game):
50
+ game.__dict__["_manifest"] = {"AppState": {"name": "Portal 2"}}
51
+ assert game.name == "Portal 2"
52
+
53
+
54
+ def test_game_name_unknown_when_manifest_missing_key(game):
55
+ game.__dict__["_manifest"] = {}
56
+ assert game.name == UNKNOWN_GAME_NAME
57
+
58
+
59
+ def test_game_name_unknown_when_manifest_empty(game):
60
+ game.__dict__["_manifest"] = {"AppState": {}}
61
+ assert game.name == UNKNOWN_GAME_NAME
@@ -0,0 +1,112 @@
1
+ from pathlib import Path
2
+ from unittest.mock import MagicMock, patch
3
+
4
+ from steam_client.login_users import LoginUser, LoginUsers, User, UserData, STEAM64_OFFSET
5
+
6
+
7
+ FAKE_STEAM_ID3 = 12345678
8
+ FAKE_USER_ID = STEAM64_OFFSET + FAKE_STEAM_ID3
9
+
10
+
11
+ def test_login_user_is_most_recent(login_user):
12
+ assert login_user.is_most_recent is True
13
+
14
+
15
+ def test_login_user_not_most_recent(steam, user_data):
16
+ data = UserData(
17
+ AccountName=user_data.AccountName,
18
+ PersonaName=user_data.PersonaName,
19
+ RememberPassword=user_data.RememberPassword,
20
+ WantsOfflineMode=user_data.WantsOfflineMode,
21
+ SkipOfflineModeWarning=user_data.SkipOfflineModeWarning,
22
+ AllowAutoLogin=user_data.AllowAutoLogin,
23
+ MostRecent="0",
24
+ Timestamp=user_data.Timestamp,
25
+ )
26
+ user = User(id=FAKE_USER_ID, data=data)
27
+ lu = LoginUser(steam, user)
28
+ assert lu.is_most_recent is False
29
+
30
+
31
+ def test_steam_id3(login_user):
32
+ assert login_user.steam_id3 == FAKE_STEAM_ID3
33
+
34
+
35
+ def test_user_data_dir(login_user):
36
+ assert login_user.user_data_dir == Path("/fake/steam/userdata") / str(FAKE_STEAM_ID3)
37
+
38
+
39
+ def test_config(login_user):
40
+ assert login_user.config == login_user.user_data_dir / "config"
41
+
42
+
43
+ def test_shortcuts_file(login_user):
44
+ assert login_user.shortcuts_file == login_user.config / "shortcuts.vdf"
45
+
46
+
47
+ def test_grid_path(login_user):
48
+ assert login_user.grid_path == login_user.user_data_dir / "config" / "grid"
49
+
50
+
51
+ def test_shortcuts_parses_vdf(steam, login_user):
52
+ fake_shortcut_data = {
53
+ "shortcuts": {
54
+ "0": {
55
+ "appid": 0,
56
+ "appname": "My Game",
57
+ "exe": "/path/game.exe",
58
+ "StartDir": "/path",
59
+ "LaunchOptions": "",
60
+ "icon": "",
61
+ "tags": {},
62
+ }
63
+ }
64
+ }
65
+ with patch("builtins.open", MagicMock()), \
66
+ patch("vdf.binary_load", return_value=fake_shortcut_data):
67
+ shortcuts = login_user.shortcuts()
68
+ assert len(shortcuts) == 1
69
+ assert shortcuts[0].name == "My Game"
70
+
71
+
72
+ def test_login_users_most_recent(steam, user_data):
73
+ recent_user = User(id=FAKE_USER_ID, data=user_data)
74
+ not_recent_data = UserData(
75
+ AccountName="other",
76
+ PersonaName="Other",
77
+ RememberPassword="0",
78
+ WantsOfflineMode="0",
79
+ SkipOfflineModeWarning="0",
80
+ AllowAutoLogin="0",
81
+ MostRecent="0",
82
+ Timestamp="0",
83
+ )
84
+ not_recent_user = User(id=FAKE_USER_ID + 1, data=not_recent_data)
85
+ users = [LoginUser(steam, recent_user), LoginUser(steam, not_recent_user)]
86
+
87
+ with patch.object(LoginUsers, "users", return_value=users):
88
+ lu = LoginUsers(steam)
89
+ most_recent = lu.most_recent_user()
90
+
91
+ assert most_recent is not None
92
+ assert most_recent.is_most_recent is True
93
+
94
+
95
+ def test_login_users_most_recent_none_when_no_recent(steam, user_data):
96
+ data = UserData(
97
+ AccountName="other",
98
+ PersonaName="Other",
99
+ RememberPassword="0",
100
+ WantsOfflineMode="0",
101
+ SkipOfflineModeWarning="0",
102
+ AllowAutoLogin="0",
103
+ MostRecent="0",
104
+ Timestamp="0",
105
+ )
106
+ users = [LoginUser(steam, User(id=FAKE_USER_ID, data=data))]
107
+
108
+ with patch.object(LoginUsers, "users", return_value=users):
109
+ lu = LoginUsers(steam)
110
+ most_recent = lu.most_recent_user()
111
+
112
+ assert most_recent is None
@@ -0,0 +1,69 @@
1
+ from pathlib import Path
2
+ import pytest
3
+
4
+ import pycrc.algorithms as crc
5
+
6
+ from steam_client.shortcut import Shortcut
7
+
8
+
9
+ @pytest.fixture
10
+ def shortcut(steam, login_user, shortcut_entry):
11
+ return Shortcut(steam, login_user, shortcut_entry)
12
+
13
+
14
+ def _compute_appid(exe: str, appname: str) -> str:
15
+ algorithm = crc.Crc(
16
+ width=32,
17
+ poly=0x04C11DB7,
18
+ reflect_in=True,
19
+ xor_in=0xFFFFFFFF,
20
+ reflect_out=True,
21
+ xor_out=0xFFFFFFFF,
22
+ )
23
+ input_string = exe + appname
24
+ top_32 = algorithm.bit_by_bit(input_string) | 0x80000000
25
+ full_64 = (top_32 << 32) | 0x02000000
26
+ return str(full_64)
27
+
28
+
29
+ def test_shortcut_name(shortcut):
30
+ assert shortcut.name == "Test Game"
31
+
32
+
33
+ def test_shortcut_appid_is_string(shortcut):
34
+ assert isinstance(shortcut.appid, str)
35
+
36
+
37
+ def test_shortcut_appid_matches_crc(shortcut, shortcut_entry):
38
+ expected = _compute_appid(shortcut_entry["exe"], shortcut_entry["appname"])
39
+ assert shortcut.appid == expected
40
+
41
+
42
+ def test_shortcut_short_id(shortcut):
43
+ expected = str(int(shortcut.appid) >> 32)
44
+ assert shortcut._short_id() == expected
45
+
46
+
47
+ def test_shortcut_icon_uses_data_icon(shortcut, shortcut_entry):
48
+ assert shortcut.icon == Path(shortcut_entry["icon"])
49
+
50
+
51
+ def test_shortcut_header(shortcut, login_user):
52
+ short_id = shortcut._short_id()
53
+ assert shortcut.header == login_user.grid_path / f"{short_id}.png"
54
+
55
+
56
+ def test_shortcut_grid(shortcut, login_user):
57
+ short_id = shortcut._short_id()
58
+ assert shortcut.grid == login_user.grid_path / f"{short_id}p.png"
59
+
60
+
61
+ def test_shortcut_hero(shortcut, login_user):
62
+ short_id = shortcut._short_id()
63
+ assert shortcut.hero == login_user.grid_path / f"{short_id}_hero.png"
64
+
65
+
66
+ def test_shortcut_repr(shortcut):
67
+ r = repr(shortcut)
68
+ assert "Shortcut" in r
69
+ assert "Test Game" in r
@@ -0,0 +1,34 @@
1
+ from pathlib import Path
2
+
3
+ from steam_client.steam import Steam, WIN_STEAM_PATH
4
+
5
+
6
+ def test_steam_default_path():
7
+ s = Steam()
8
+ assert s.base_path == WIN_STEAM_PATH
9
+
10
+
11
+ def test_steam_custom_path():
12
+ s = Steam("/custom/path")
13
+ assert s.base_path == "/custom/path"
14
+
15
+
16
+ def test_steam_repr():
17
+ s = Steam("/custom/path")
18
+ assert repr(s) == "Steam(base_path='/custom/path')"
19
+
20
+
21
+ def test_app_cache(steam):
22
+ assert steam.app_cache == Path("/fake/steam/appcache")
23
+
24
+
25
+ def test_user_data(steam):
26
+ assert steam.user_data == Path("/fake/steam/userdata")
27
+
28
+
29
+ def test_library_folders(steam):
30
+ assert steam.library_folders == Path("/fake/steam/config/libraryfolders.vdf")
31
+
32
+
33
+ def test_library_cache(steam):
34
+ assert steam.library_cache == Path("/fake/steam/appcache/librarycache")