forked from Open-Wine-Components/umu-protonfixes
-
Notifications
You must be signed in to change notification settings - Fork 0
/
protonfixes_test.py
408 lines (345 loc) · 15 KB
/
protonfixes_test.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
import unittest
import os
import tempfile
from pathlib import Path
from unittest.mock import patch
import io
import urllib.request
import fix
class TestProtonfixes(unittest.TestCase):
def setUp(self):
self.env = {
'STORE': '',
'SteamAppId': '',
'SteamGameId': '',
'STEAM_COMPAT_DATA_PATH': '',
'UMU_ID': '',
'DEBUG': '',
}
self.game_id = '1293820'
self.pfx = Path(tempfile.mkdtemp())
def tearDown(self):
for key in self.env:
if key in os.environ:
os.environ.pop(key)
if self.pfx.is_dir():
if self.pfx.joinpath('steamapps').is_dir():
self.pfx.joinpath('steamapps', 'appmanifest_1628350.acf').unlink(
missing_ok=True
)
self.pfx.joinpath('steamapps').rmdir()
self.pfx.joinpath('game_title').unlink(missing_ok=True)
self.pfx.rmdir()
def testModuleName(self):
"""Pass a non-numeric game id
Expects a string that refers to a module in gamefixes-umu
"""
game_id = 'umu-default'
result = fix.get_module_name(game_id)
self.assertEqual(result, 'protonfixes.gamefixes-umu.umu-default')
def testModuleNameNum(self):
"""Pass a numeric game id
In this case, it's assumed the game is from Steam when the game id is
numeric
Expects a string that refers to a module in gamefixes-steam
"""
game_id = '1091500'
result = fix.get_module_name(game_id)
self.assertEqual(result, f'protonfixes.gamefixes-steam.{game_id}')
def testModuleNameNoneAndNumeric(self):
"""Pass a numeric gameid and set STORE
In this case, when the game id is numeric, we always refer to a
module in the gamefixes-steam.
"""
game_id = '1091500'
os.environ['STORE'] = 'none'
result = fix.get_module_name(game_id)
self.assertEqual(result, f'protonfixes.gamefixes-steam.{game_id}')
def testModuleNameStoreAndNumeric(self):
"""Pass a numeric gameid and set STORE
In this case, when the game id is numeric, we always refer to a
module in gamefixes-steam
When passed a valid store, that value should not be used
"""
game_id = '1091500'
os.environ['STORE'] = 'gog'
result = fix.get_module_name(game_id)
self.assertEqual(result, f'protonfixes.gamefixes-steam.{game_id}')
def testModuleNameStore(self):
"""Pass a non-numeric game id and setting valid STORE
For non-numeric game ids, the umu database should always be referenced
Expects a string that refers to a module in gamefixes-$STORE
"""
os.environ['STORE'] = 'GOG'
game_id = 'umu-1091500'
result = fix.get_module_name(game_id)
self.assertEqual(result, f'protonfixes.gamefixes-gog.{game_id}')
def testModuleNameNoStore(self):
"""Pass a non-numeric game id and setting an invalid STORE
Expects a string that refers to a module in gamefixes-umu
"""
os.environ['STORE'] = 'foo'
game_id = 'umu-1091500'
result = fix.get_module_name(game_id)
self.assertEqual(result, f'protonfixes.gamefixes-umu.{game_id}')
def testModuleNameStoreEmpty(self):
"""Pass a non-numeric game id and setting an empty store
Expects a string that refers to a module in gamefixes-umu
"""
os.environ['STORE'] = ''
game_id = 'umu-1091500'
result = fix.get_module_name(game_id)
self.assertEqual(result, f'protonfixes.gamefixes-umu.{game_id}')
def testModuleNameEmpty(self):
"""Pass empty strings for the game id and store"""
os.environ['STORE'] = ''
game_id = ''
result = fix.get_module_name(game_id)
# TODO Handle the empty string case in fix.py
# While umu enforces setting a gameid, it would still be a good idea
# to handle this case
self.assertEqual(result, 'protonfixes.gamefixes-umu.')
def testModuleNameDefault(self):
"""Pass a gameid and default=True"""
game_id = '1091500'
result = fix.get_module_name(game_id, default=True)
self.assertEqual(result, 'protonfixes.gamefixes-steam.default')
def testModuleNameLocal(self):
"""Pass a gameid and local=True"""
game_id = '1091500'
result = fix.get_module_name(game_id, local=True)
self.assertEqual(result, f'localfixes.{game_id}')
def testModuleNameLocalDefault(self):
"""Pass a gameid and set local=True,default=True
In this case, the game id will be completely ignored
"""
game_id = '1091500'
result = fix.get_module_name(game_id, local=True, default=True)
self.assertEqual(result, 'localfixes.default')
def testGetGameSteamAppId(self):
"""Only set the SteamAppId
Protonfixes depends on being supplied an app id when applying fixes
to games.
This appid is typically set by a client application, but the user can
set it in some cases (e.g., umu-launcher).
If the app id is numeric, then protonfixes will refer to the
gamefixes-steam folder. Otherwise, the STORE environment variable will
be used to determine which fix will be applied.
"""
os.environ['SteamAppId'] = self.game_id
result = fix.get_game_id()
self.assertEqual(result, self.game_id)
self.assertTrue(os.environ.get('SteamAppId'), 'SteamAppId was unset')
def testGetGameUmuId(self):
"""Only set the UMU_ID"""
os.environ['UMU_ID'] = self.game_id
result = fix.get_game_id()
self.assertEqual(result, self.game_id)
self.assertTrue(os.environ.get('UMU_ID'), 'UMU_ID was unset')
def testGetGameSteamGameId(self):
"""Only set the SteamGameId"""
os.environ['SteamGameId'] = self.game_id
result = fix.get_game_id()
self.assertEqual(result, self.game_id)
self.assertTrue(os.environ.get('SteamGameId'), 'SteamGameId was unset')
def testGetGameCompatPath(self):
"""Only set the STEAM_COMPAT_DATA_PATH"""
os.environ['STEAM_COMPAT_DATA_PATH'] = self.game_id
result = fix.get_game_id()
self.assertEqual(result, self.game_id)
self.assertTrue(
os.environ.get('STEAM_COMPAT_DATA_PATH'), 'STEAM_COMPAT_DATA_PATH was unset'
)
def testGetGameNone(self):
"""Set no environment variables
Expect None to be returned
"""
func = fix.get_game_id.__wrapped__ # Do not reference the cache
self.assertTrue(
'STEAM_COMPAT_DATA_PATH' not in os.environ, 'STEAM_COMPAT_DATA_PATH is set'
)
self.assertTrue('SteamGameId' not in os.environ, 'SteamGameId is set')
self.assertTrue('UMU_ID' not in os.environ, 'UMU_ID is set')
self.assertTrue('SteamAppId' not in os.environ, 'SteamAppId is set')
result = func()
self.assertFalse(result, 'None was not returned')
def testGetStoreNameZoom(self):
"""Pass zoomplatform as store name
The get_store_name function returns a string associated with a
supported store in the umu database.
The string will be used to display a message in the console to let the
user know which fix will be applied.
"""
store = 'zoomplatform'
result = fix.get_store_name(store)
self.assertIsInstance(result, str, 'Expected a str')
self.assertTrue(result, 'Expected a value')
def testGetStoreNameAmazon(self):
"""Pass amazon as store name"""
store = 'amazon'
result = fix.get_store_name(store)
self.assertIsInstance(result, str, 'Expected a str')
self.assertTrue(result, 'Expected a value')
def testGetStoreNameEA(self):
"""Pass ea as store name"""
store = 'ea'
result = fix.get_store_name(store)
self.assertTrue(result, 'Expected a value')
def testGetStoreNameEGS(self):
"""Pass egs as store name"""
store = 'egs'
result = fix.get_store_name(store)
self.assertIsInstance(result, str, 'Expected a str')
self.assertTrue(result, 'Expected a value')
def testGetStoreNameGOG(self):
"""Pass gog as store name"""
store = 'gog'
result = fix.get_store_name(store)
self.assertIsInstance(result, str, 'Expected a str')
self.assertTrue(result, 'Expected a value')
def testGetStoreNameHumble(self):
"""Pass humble as store name"""
store = 'humble'
result = fix.get_store_name(store)
self.assertIsInstance(result, str, 'Expected a str')
self.assertTrue(result, 'Expected a value')
def testGetStoreNameItchio(self):
"""Pass itchio as store name"""
store = 'itchio'
result = fix.get_store_name(store)
self.assertIsInstance(result, str, 'Expected a str')
self.assertTrue(result, 'Expected a value')
def testGetStoreNameSteam(self):
"""Pass steam as store name"""
store = 'steam'
result = fix.get_store_name(store)
self.assertIsInstance(result, str, 'Expected a str')
self.assertTrue(result, 'Expected a value')
def testGetStoreNameUbisoft(self):
"""Pass ubisoft as store name"""
store = 'ubisoft'
result = fix.get_store_name(store)
self.assertIsInstance(result, str, 'Expected a str')
self.assertTrue(result, 'Expected a value')
def testGetStoreNameEmpty(self):
"""Pass an empty string as store name"""
store = ''
result = fix.get_store_name(store)
self.assertFalse(result, 'Expected None')
def testGetStoreNameFoo(self):
"""Pass a store that is not supported in umu"""
store = 'jastusa'
result = fix.get_store_name(store)
self.assertFalse(result, 'Expected None')
def testGetGameName(self):
"""Set UMU_ID and access the game_title file for its title
The get_game_name function returns a string of the running game's
title.
It checks a few system paths in the user's system to try to
determine it, and makes a callout to an owc endpoint to get an
official title by its UMU_ID.
"""
os.environ['UMU_ID'] = self.game_id
os.environ['WINEPREFIX'] = self.pfx.as_posix()
self.pfx.joinpath('game_title').touch()
result = fix.get_game_name()
self.assertFalse(result, 'Expected an empty string')
def testGetGameNameDB(self):
"""Set UMU_ID and access umu database
Makes a request to the umu database for a title name to be displayed
if a UMU_ID is supplied. This function should be fail safe in case of
a TimeoutError, OSError, IndexError or UnicodeDecodeError
"""
os.environ['UMU_ID'] = 'umu-35140'
os.environ['WINEPREFIX'] = self.pfx.as_posix()
# Mock a valid umu db response
data = '[{"title":"Batman: Arkham Asylum Game of the Year Edition","umu_id":"umu-35140","acronym":null,"codename":"1482504285","store":"gog","notes":null},{"title":"Batman: Arkham Asylum Game of the Year Edition","umu_id":"umu-35140","acronym":null,"codename":"godwit","store":"egs","notes":null}]'
data = io.StringIO(data)
with (
patch.object(fix, 'check_internet', return_value=True),
patch.object(urllib.request, 'urlopen', return_value=data),
):
func = fix.get_game_name.__wrapped__ # Do not reference the cache
result = func()
self.assertEqual(result, 'Batman: Arkham Asylum Game of the Year Edition')
def testGetGameNameDBTimeout(self):
"""Set UMU_ID and access umu database
Mock the TimeoutError
"""
os.environ['UMU_ID'] = 'umu-35140'
os.environ['WINEPREFIX'] = self.pfx.as_posix()
# Mock a valid umu db response
with (
patch.object(fix, 'check_internet', return_value=True),
patch.object(urllib.request, 'urlopen') as mock_function,
):
mock_function.side_effect = TimeoutError
func = fix.get_game_name.__wrapped__ # Do not reference the cache
result = func()
self.assertEqual(result, 'UNKNOWN')
def testGetGameNameDbOS(self):
"""Set UMU_ID and access umu database
Mock the OSError, which only shown if debugging is enabled
"""
os.environ['UMU_ID'] = 'umu-35140'
os.environ['WINEPREFIX'] = self.pfx.as_posix()
os.environ['DEBUG'] = '1'
# Mock a valid umu db response
with (
patch.object(fix, 'check_internet', return_value=True),
patch.object(urllib.request, 'urlopen') as mock_function,
):
mock_function.side_effect = OSError
func = fix.get_game_name.__wrapped__ # Do not reference the cache
result = func()
self.assertEqual(result, 'UNKNOWN')
def testGetGameNameDbIndex(self):
"""Set UMU_ID and access umu database
Mock the IndexError
"""
os.environ['UMU_ID'] = 'umu-35140'
os.environ['WINEPREFIX'] = self.pfx.as_posix()
os.environ['DEBUG'] = '1'
# Mock a valid umu db response
with (
patch.object(fix, 'check_internet', return_value=True),
patch.object(urllib.request, 'urlopen') as mock_function,
):
mock_function.side_effect = IndexError
func = fix.get_game_name.__wrapped__ # Do not reference the cache
result = func()
self.assertEqual(result, 'UNKNOWN')
def testGetGameNameDbUnicode(self):
"""Set UMU_ID and access umu database
Mock the UnicodeError
"""
os.environ['UMU_ID'] = 'umu-35140'
os.environ['WINEPREFIX'] = self.pfx.as_posix()
os.environ['DEBUG'] = '1'
def mock_urlopen_raise_error(*args, **kwargs):
raise UnicodeDecodeError('utf-8', b'', 0, 1, '')
# Mock a valid umu db response
with (
patch.object(fix, 'check_internet', return_value=True),
patch.object(urllib.request, 'urlopen') as mock_function,
):
mock_function.side_effect = mock_urlopen_raise_error
func = fix.get_game_name.__wrapped__ # Do not reference the cache
result = func()
self.assertEqual(result, 'UNKNOWN')
def testGetGameNameNoManifest(self):
"""Do not set UMU_ID and try to get the title from the steam app
library
UNKNOWN should be returned because no manifest file will exist in the
test directory
"""
os.environ['SteamAppId'] = '1628350'
os.environ['WINEPREFIX'] = self.pfx.as_posix()
os.environ['PWD'] = os.environ['WINEPREFIX']
steamapps = self.pfx.joinpath('steamapps')
steamapps.mkdir()
func = fix.get_game_name.__wrapped__ # Do not reference the cache
result = func()
self.assertEqual(result, 'UNKNOWN')
if __name__ == '__main__':
unittest.main()