Skip to content

Commit

Permalink
Merge branch 'master' into Win_version
Browse files Browse the repository at this point in the history
sharkwouter authored Sep 19, 2022
2 parents b09a806 + 343ba01 commit a3aa41b
Showing 23 changed files with 1,668 additions and 171 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/pythontests.yml
Original file line number Diff line number Diff line change
@@ -25,4 +25,5 @@ jobs:
flake8 . --count --show-source --statistics --exit-zero
- name: Run unit tests
run: |
python -m unittest tests/*.py
python3 -m coverage run --source minigalaxy -m unittest discover -v tests && python3 -m coverage report -m
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -28,8 +28,8 @@ jobs:
run: |
git config --global user.name 'Wouter Wijsman'
git config --global user.email '[email protected]'
git add data/io.github.sharkwouter.Minigalaxy.metainfo.xml debian/changelog
git commit -m "Update changelogs for Debian and Flatpak"
git add pyproject.toml data/io.github.sharkwouter.Minigalaxy.metainfo.xml debian/changelog
git commit -m "Update changelogs and pyproject.toml to new release"
git push
- name: Release
uses: softprops/action-gh-release@v1
34 changes: 34 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,39 @@
**1.2.0**
- Split game information and properties into different windows (thanks to TotalCaesar659)
- Add list view (thanks to TotalCaesar659)
- Allow DLC to be queued up for downloading (thanks to flagrama)
- Fix changing library to a directory with special characters in the name (thanks to makson96)
- Fix signing in with Facebook (thanks to phlash)
- Always use cached DLC icons and thumbnails (thanks to TotalCaesar659)
- Cache information covers (thanks to TotalCaesar659)
- Fix installers not being cleaned up like expected (thanks to Kzimir)
- Fix error when opening game properties window when wine is not installed (thanks to lmeunier)
- Fix freeze for games generating a lot of output (thanks to lmeunier)
- Fix extracting rar based games with innoextract (thanks to Kzimir)
- Allow setting wine executable per game (thanks to Kzimir)
- Add GameMode support (thanks to TotalCaesar659)
- Add MangoHud support (thanks to TotalCaesar659)
- Add option to use Winetricks (thanks to TotalCaesar659)
- Fix updates not always being detected directly after opening Minigalaxy (thanks to TotalCaesar659)
- Fix desktop files generated not always being able to launch (thanks to otaconix)
- Show percentage when hovering over download progress bar (thanks to TotalCaesar659)
- Add option to disable update check per game (thanks to TotalCaesar659)
- Add forum, GOG Database and PCGamingWiki URLs to game information (thanks to TotalCaesar659)
- List genre as unknown in game information when none is found (thanks to mareksapota)
- Fix changing installation path causing crashes in rare cases (thanks to makson96)
- Fall back to English when locale cannot be determined (thanks to flagrama)
- Add gettext to build dependencies (thanks to larslindq)
- Improve error handling upon API errors
- Fix several issues with launching Windows games from Minigalaxy
- Fix some games getting stuck on in queue
- Fix Windows game installation not caring about preferred language (thanks to Kzimir)

- Add Greek translation (thanks to Pyrofanis)
- Add Spanish (Spain) translation (thanks to mbarrio)
- Add Romanian (Romania) translation (thanks to xSlendiX)

- Update Norwegian Bokmål translation (thanks to kimmalmo)
- Update Czech translation (thanks to jakbuz23)

**1.1.0**
- Improve integrity check after downloading (thanks to makson96)
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -201,6 +201,10 @@ Special thanks goes out to all contributors:
- stephanlachnit for upstreaming to Debian and multiple code contributions
- sgn for fixing a bug
- otaconix for fixing a bug
- phlash for fixing a bug
- mareksapota for fixing a bug
- zocker-160 for code cleanup
- waltercool for contributing code
- s8321414 for translating to Taiwanese Mandarin
- fuzunspm for translating to Turkish
- thomansb22 for translating to French
@@ -212,6 +216,8 @@ Special thanks goes out to all contributors:
- dummyx for translating to simplified Chinese
- juanborda, advy99 and LocalPinkRobin for translating to Spanish
- Newbytee for translating to Swedish
- Pyrofanis for translating to Greek
- mbarrio for translating to Spanish
- jubalh for packaging Minigalaxy for openSUSE
- gasinvein for packaging Minigalaxy for flathub
- metafarion for packaging Minigalaxy for Gentoo
239 changes: 162 additions & 77 deletions data/po/cs_CZ.po

Large diffs are not rendered by default.

619 changes: 619 additions & 0 deletions data/po/el.po

Large diffs are not rendered by default.

520 changes: 520 additions & 0 deletions data/po/ro.po

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion data/ui/about.ui
Original file line number Diff line number Diff line change
@@ -22,8 +22,13 @@
<a href="https://github.com/stephanlachnit">Stephan Lachnit</a>
<a href="https://github.com/graag">Konrad Klimaszewski</a>
<a href="https://github.com/lmeunier">Laurent Meunier</a>
&lt;a href="https://github.com/zweif"&gt;zweif&lt;/a&gt;</property>
&lt;a href="https://github.com/zweif"&gt;zweif&lt;/a&gt;
&lt;a href="https://github.com/phlash"&gt;Phil Ashby&lt;/a&gt;
&lt;a href="https://github.com/mareksapota"&gt;Marek Sapota&lt;/a&gt;
&lt;a href="https://github.com/zocker-160"&gt;zocker-160&lt;/a&gt;
&lt;a href="https://github.com/waltercool"&gt;WalterCool&lt;/a&gt;</property>
<property name="translator-credits">&lt;a href="https://github.com/ArturWroblewski"&gt;Artur Wróblewski&lt;/a&gt;
&lt;a href="https://github.com/Pyrofani"&gt;Athanasios Nektarios Karachalios Stagkas&lt;/a&gt;
&lt;a href="https://github.com/BlindJerobine"&gt;BlindJerobine&lt;/a&gt;
&lt;a href="https://github.com/EsdrasTarsis"&gt;Esdras Tarsis&lt;/a&gt;
&lt;a href="https://github.com/fuzunspm"&gt;Hüseyin Fahri Uzun&lt;/a&gt;
@@ -35,6 +40,7 @@
&lt;a href="https://github.com/tim77"&gt;Artem Polishchuk&lt;/a&gt;
&lt;a href="https://github.com/dummyx"&gt;dummyx&lt;/a&gt;
&lt;a href="https://github.com/juanborda"&gt;JB&lt;/a&gt;
&lt;a href="https://github.com/mbarrio"&gt;Miguel Barrio Orsikowsky&lt;/a&gt;
&lt;a href="https://github.com/Newbytee"&gt;Newbytee&lt;/a&gt;
&lt;a href="https://github.com/jakbuz23"&gt;jakbuz23&lt;/a&gt;
&lt;a href="https://github.com/heidiwenger"&gt;heidiwenger&lt;/a&gt;
101 changes: 53 additions & 48 deletions minigalaxy/api.py
Original file line number Diff line number Diff line change
@@ -4,6 +4,8 @@
from urllib.parse import urlencode
import requests
import xml.etree.ElementTree as ET

from minigalaxy.file_info import FileInfo
from minigalaxy.game import Game
from minigalaxy.constants import IGNORE_GAME_IDS, SESSION
from minigalaxy.config import Config
@@ -169,64 +171,58 @@ def get_download_info(self, game: Game, operating_system="", dlc_installers="")
def get_real_download_link(self, url):
return self.__request(url)['downlink']

def get_download_file_md5(self, url):
def get_download_file_info(self, url):
"""
Returns a download file's md5 sum
Returns an empty string if anything goes wrong
Returns some information about a downloadable file based on an XML file offered by GOG
:param url: Url to get download and checksum links from the API
:return: the md5 sum as string
:return: a FileInfo object with md5 set to the md5 or and empty string and size set to the file size or 0
"""
result = ""
checksum_data = self.__request(url)
if 'checksum' in checksum_data.keys() and len(checksum_data['checksum']) > 0:
xml_data = self.__get_xml_checksum(checksum_data['checksum'])
if "md5" in xml_data.keys() and len(xml_data["md5"]) > 0:
result = xml_data["md5"]

if not result:
file_info = FileInfo(md5="", size=0)
try:
checksum_data = self.__request(url)
if 'checksum' in checksum_data.keys() and len(checksum_data['checksum']) > 0:
xml_data = self.__get_xml_checksum(checksum_data['checksum'])
if "md5" in xml_data.keys() and len(xml_data["md5"]) > 0:
file_info.md5 = xml_data["md5"]
if "total_size" in xml_data.keys() and len(xml_data["total_size"]) > 0:
file_info.size = int(xml_data["total_size"])
except requests.exceptions.RequestException as e:
print("Couldn't retrieve file info. Encountered HTTP exception: {}".format(e))

if not file_info.md5:
print("Couldn't find md5 in xml checksum data")

return result

def get_file_size(self, url):
"""
Returns the file size according to an XML file offered by GOG
Returns 0 if anything goes wrong
:param url: Url to get download and checksum links from the API
:return: probable file size in bytes as int
"""
result = 0
checksum_data = self.__request(url)
if 'checksum' in checksum_data.keys() and len(checksum_data['checksum']) > 0:
xml_data = self.__get_xml_checksum(checksum_data['checksum'])
if "total_size" in xml_data.keys() and int(xml_data["total_size"]) > 0:
result = int(xml_data["total_size"])

if not result:
if not file_info.size:
print("Couldn't find file size in xml checksum data")

return result
return file_info

def __get_xml_checksum(self, url):
@staticmethod
def __get_xml_checksum(url):
result = {}
response = SESSION.get(url)
if response.status_code == http.HTTPStatus.OK and len(response.text) > 0:
response_object = ET.fromstring(response.text)
if response_object and response_object.attrib:
result = response_object.attrib
else:
print("Couldn't read xml data. Response with code {} received with the following content: {}".format(
response.status_code, response.text
))
return result
try:
response = SESSION.get(url)
if response.status_code == http.HTTPStatus.OK and len(response.text) > 0:
response_object = ET.fromstring(response.text)
if response_object and response_object.attrib:
result = response_object.attrib
else:
print("Couldn't read xml data. Response with code {} received with the following content: {}".format(
response.status_code, response.text
))
except requests.exceptions.RequestException as e:
print("Couldn't read xml data. Received RequestException : {}".format(e))
finally:
return result

def get_user_info(self) -> str:
username = Config.get("username")
if not username:
url = "https://embed.gog.com/userData.json"
response = self.__request(url)
username = response["username"]
Config.set("username", username)
if "username" in response.keys():
username = response["username"]
Config.set("username", username)
return username

def get_download_version(self, game: Game):
@@ -279,13 +275,22 @@ def __request(self, url: str = None, params: dict = None) -> dict:
headers = {
'Authorization': "Bearer {}".format(str(self.active_token)),
}
response = SESSION.get(url, headers=headers, params=params)
if self.debug:
result = {}
try:
response = SESSION.get(url, headers=headers, params=params)
if self.debug:
print("Request: {}".format(url))
print("Return code: {}".format(response.status_code))
print("Response body: {}".format(response.text))
print("")
if response.status_code < 300:
result = response.json()
except requests.exceptions.RequestException as e:
print("Encountered exception while making HTTP request.")
print("Request: {}".format(url))
print("Return code: {}".format(response.status_code))
print("Response body: {}".format(response.text))
print("Exception: {}".format(e))
print("")
return response.json()
return result

@staticmethod
def __request_gamesdb(game: Game):
3 changes: 3 additions & 0 deletions minigalaxy/constants.py
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@
["es", _("Spanish")],
["sv", _("Swedish")],
["tr", _("Turkish")],
["ro", _("Romanian")],
]

SUPPORTED_LOCALES = [
@@ -46,6 +47,8 @@
["zh_TW", _("Traditional Chinese")],
["tr", _("Turkish")],
["uk", _("Ukrainian")],
["el", _("Greek")],
["ro", _("Romanian")],
]

VIEWS = [
4 changes: 2 additions & 2 deletions minigalaxy/download_manager.py
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@
import threading
import queue

from requests.exceptions import ConnectionError
from requests.exceptions import RequestException
from minigalaxy.config import Config
from minigalaxy.constants import DOWNLOAD_CHUNK_SIZE, MINIMUM_RESUME_SIZE, SESSION
from minigalaxy.download import Download
@@ -85,7 +85,7 @@ def __download_file(self, download):
start_point, download_mode = self.get_start_point_and_download_mode(download)
result = self.download_operation(download, start_point, download_mode)
break
except ConnectionError as e:
except RequestException as e:
print(e)
download_attempt += 1
# Successful downloads
7 changes: 7 additions & 0 deletions minigalaxy/file_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class FileInfo:
"""
Just a container for the md5 checksum and file size of a downloadable file
"""
def __init__(self, md5, size):
self.md5 = md5
self.size = size
29 changes: 28 additions & 1 deletion minigalaxy/installer.py
Original file line number Diff line number Diff line change
@@ -42,6 +42,7 @@ def check_diskspace(required_size, location):
def install_game(game, installer): # noqa: C901
error_message = ""
tmp_dir = ""
print("Installing {}".format(game.name))
if not error_message:
error_message = verify_installer_integrity(game, installer)
if not error_message:
@@ -141,7 +142,8 @@ def extract_windows(game, installer, temp_dir):
def extract_by_innoextract(installer, temp_dir):
err_msg = ""
if shutil.which("innoextract"):
cmd = ["innoextract", installer, "-d", temp_dir, "--gog"]
lang = lang_install(installer)
cmd = ["innoextract", installer, "-d", temp_dir, "--gog", lang]
stdout, stderr, exitcode = _exe_cmd(cmd)
if exitcode not in [0]:
err_msg = _("Innoextract extraction failed.")
@@ -334,3 +336,28 @@ def _mv(source_dir, target_dir):
if os.path.exists(dst_file):
os.remove(dst_file)
shutil.move(file_to_copy, destination_dir)


# Some installers allow to choose game's language before installation (Divinity Original Sin or XCom EE / XCom 2)
# "--list-languages" option returns "en-US", "fr-FR" etc... for these games.
# Others installers return "French : Français" but disallow to choose game's language before installation
def lang_install(installer):
languages = []
arg = ""
process = subprocess.Popen(["innoextract", installer, "--list-languages"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = process.communicate()
output = stdout.decode("utf-8")

for line in output.split('\n'):
if not line.startswith(' -'):
continue
languages.append(line[3:])
for lang in languages:
if "-" in lang: # lang must be like "en-US" only.
if Config.get("lang") == lang[0:2]:
arg = "--language={}".format(lang)
break
else:
arg = "--language=en-US"
break
return arg
25 changes: 16 additions & 9 deletions minigalaxy/launcher.py
Original file line number Diff line number Diff line change
@@ -76,6 +76,7 @@ def get_execute_command(game) -> list:
exe_cmd.insert(0, "mangohud")
exe_cmd.insert(1, "--dlsym")
exe_cmd = get_exe_cmd_with_var_command(game, exe_cmd)
print("Launch command for {}: {}".format(game.name, " ".join(exe_cmd)))
return exe_cmd


@@ -115,17 +116,23 @@ def get_windows_exe_cmd(game, files):

# Find game executable file
for file in files:
if re.match(r'^goggame-[0-9]*\.info$', file):
if re.match(r'^goggame-\d*\.info$', file):
os.chdir(game.install_dir)
with open(file, 'r') as info_file:
info = json.loads(info_file.read())
# if we have the workingDir property, start the executable at that directory
if info["playTasks"]:
if "workingDir" in info["playTasks"][0] and info["playTasks"][0]["workingDir"]:
exe_cmd = [get_wine_path(game), "start", "/b", "/wait", "/d", info["playTasks"][0]["workingDir"],
info["playTasks"][0]["path"]]
else:
exe_cmd = [get_wine_path(game), info["playTasks"][0]["path"]]
if "playTasks" in info:
for task in info["playTasks"]:
if "isPrimary" not in task or not task["isPrimary"]:
continue
if "category" in task and task["category"] == "game" and "path" in task:
working_dir = task["workingDir"] if "workingDir" in task else "."
path = task["path"]
exe_cmd = [get_wine_path(game), "start", "/b", "/wait", "/d", working_dir,
path]
if "arguments" in task:
exe_cmd += task["arguments"].split(" ")
break
if exe_cmd == [""]:
# in case no goggame info file was found
executables = glob.glob(game.install_dir + '/*.exe')
@@ -140,9 +147,9 @@ def get_dosbox_exe_cmd(game, files):
dosbox_config = ""
dosbox_config_single = ""
for file in files:
if re.match(r'^dosbox_?([a-z]|[A-Z]|[0-9])+\.conf$', file):
if re.match(r'^dosbox_?([a-z]|[A-Z]|\d)+\.conf$', file):
dosbox_config = file
if re.match(r'^dosbox_?([a-z]|[A-Z]|[0-9])+_single\.conf$', file):
if re.match(r'^dosbox_?([a-z]|[A-Z]|\d)+_single\.conf$', file):
dosbox_config_single = file
print("Using system's dosbox to launch {}".format(game.name))
return ["dosbox", "-conf", dosbox_config, "-conf", dosbox_config_single, "-no-console", "-c", "exit"]
8 changes: 4 additions & 4 deletions minigalaxy/ui/gametile.py
Original file line number Diff line number Diff line change
@@ -249,7 +249,8 @@ def __download(self, download_info, finish_func, cancel_to_state):
GLib.idle_add(self.parent.parent.show_error, _("Download error"), _(str(e)))
download_success = False
break
total_file_size += self.api.get_file_size(file_info["downlink"])
info = self.api.get_download_file_info(file_info["downlink"])
total_file_size += info.size
try:
# Extract the filename from the download url (filename is between %2F and &token)
filename = urllib.parse.unquote(re.search('%2F(((?!%2F).)*)&t', download_url).group(1))
@@ -259,9 +260,8 @@ def __download(self, download_info, finish_func, cancel_to_state):
if key == 0:
# If key = 0, denote the file as the executable's path
executable_path = download_path
md5sum = self.api.get_download_file_md5(file_info["downlink"])
if md5sum:
self.game.md5sum[os.path.basename(download_path)] = md5sum
if info.md5:
self.game.md5sum[os.path.basename(download_path)] = info.md5
download = Download(
url=download_url,
save_location=download_path,
8 changes: 4 additions & 4 deletions minigalaxy/ui/gametilelist.py
Original file line number Diff line number Diff line change
@@ -252,7 +252,8 @@ def __download(self, download_info, finish_func, cancel_to_state):
GLib.idle_add(self.parent.parent.show_error, _("Download error"), _(str(e)))
download_success = False
break
total_file_size += self.api.get_file_size(file_info["downlink"])
info = self.api.get_download_file_info(file_info["downlink"])
total_file_size += info.size
try:
# Extract the filename from the download url (filename is between %2F and &token)
filename = urllib.parse.unquote(re.search('%2F(((?!%2F).)*)&t', download_url).group(1))
@@ -262,9 +263,8 @@ def __download(self, download_info, finish_func, cancel_to_state):
if key == 0:
# If key = 0, denote the file as the executable's path
executable_path = download_path
md5sum = self.api.get_download_file_md5(file_info["downlink"])
if md5sum:
self.game.md5sum[os.path.basename(download_path)] = md5sum
if info.md5:
self.game.md5sum[os.path.basename(download_path)] = info.md5
download = Download(
url=download_url,
save_location=download_path,
25 changes: 23 additions & 2 deletions minigalaxy/ui/login.py
Original file line number Diff line number Diff line change
@@ -21,10 +21,13 @@ def __init__(self, login_url=None, redirect_url=None, parent=None):

self.redirect_url = redirect_url

context = WebKit2.WebContext.new()
webview = WebKit2.WebView.new_with_context(context)
# https://stackoverflow.com/questions/9147875/webview-dont-display-javascript-windows-open
settings = WebKit2.Settings.new()
settings.props.javascript_can_open_windows_automatically = True
webview = WebKit2.WebView.new_with_settings(settings)
webview.load_uri(login_url)
webview.connect('load-changed', self.on_navigation)
webview.connect('create', self.on_create)

self.box.pack_start(webview, True, True, 0)
self.show_all()
@@ -37,6 +40,24 @@ def on_navigation(self, widget, load_event):
self.result = self.__get_code_from_url(uri)
self.hide()

# Create any pop-up windows during authentication
def on_create(self, widget, action):
popup = Gtk.Dialog(title=_("Facebook Login"), parent=self, flags=0, buttons=())
webview = WebKit2.WebView.new_with_related_view(widget)
webview.load_uri(action.get_request().get_uri())
webview.__dict__['popup'] = popup
webview.connect('close', self.on_close_popup)
popup.get_content_area().pack_start(webview, True, True, 0)
popup.set_size_request(400, 600)
popup.set_modal(True)
popup.show_all()
return webview

# When a pop up is closed (by Javascript), close the Gtk window too
def on_close_popup(self, widget):
if 'popup' in widget.__dict__:
widget.__dict__['popup'].hide()

# Return the code when can be used by the API to authenticate
def get_result(self):
return self.result
10 changes: 7 additions & 3 deletions minigalaxy/ui/window.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import os
import locale

from minigalaxy.ui.login import Login
try:
from minigalaxy.ui.login import Login
except ValueError as err:
print("Login UI disabled due lack of dependencies: {}".format(err))

from minigalaxy.ui.preferences import Preferences
from minigalaxy.ui.about import About
from minigalaxy.api import Api
@@ -71,7 +75,7 @@ def __init__(self, name="Minigalaxy"):
self.__authenticate()
self.HeaderBar.set_subtitle(self.api.get_user_info())
except Exception as e:
print(e)
print("Starting in offline mode, after receiving exception: {}".format(e))
self.offline = True
self.sync_library()

@@ -187,7 +191,7 @@ def __authenticate(self):

authenticated = self.api.authenticate(refresh_token=token, login_code=url)

while not authenticated:
while not authenticated and 'Login' in dir():
login_url = self.api.get_login_url()
redirect_url = self.api.get_redirect_url()
login = Login(login_url=login_url, redirect_url=redirect_url, parent=self)
15 changes: 15 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[project]
name = "minigalaxy"
description = "A simple GOG Linux client"
version = "1.1.0"
authors = [
{ name = "Wouter Wijsman", email = "wwijsman@live.nl" }
]
dependencies = [
"PyGObject",
"requests"
]

[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta:__legacy__"
1 change: 1 addition & 0 deletions requirements-testing.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
requests
flake8
simplejson
coverage
2 changes: 2 additions & 0 deletions scripts/create-release.sh
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@ CHANGELOG_FILE="${WORK_DIR}/CHANGELOG.md"
METADATA_FILE="${WORK_DIR}/data/io.github.sharkwouter.Minigalaxy.metainfo.xml"
RELEASE_FILE="${WORK_DIR}/release.md"
VERSION_FILE="${WORK_DIR}/minigalaxy/version.py"
TOML_FILE="${WORK_DIR}/pyproject.toml"
VERSION="$(head -1 "${CHANGELOG_FILE}"|tr -d "*")"

check_changelog() {
@@ -67,6 +68,7 @@ set_debian_changelog_release() {

set_version() {
echo "VERSION = \"${VERSION}\"" > "${VERSION_FILE}"
sed -i "s/version = .*/version = \"${VERSION}\"/" "${TOML_FILE}"
}

return_version_info() {
85 changes: 69 additions & 16 deletions tests/test_api.py
Original file line number Diff line number Diff line change
@@ -104,6 +104,7 @@ def test1_get_library(self):
response_mock = MagicMock()
response_mock.json.return_value = response_dict
m_constants.SESSION.get.return_value = response_mock
m_constants.SESSION.get().status_code = http.HTTPStatus.OK
exp = "Neverwinter Nights: Enhanced Edition"
retrieved_games, err_msg = api.get_library()
obs = retrieved_games[0].name
@@ -137,7 +138,7 @@ def test2_get_version(self):
obs = api.get_version(test_game, gameinfo=API_GET_INFO_TOONSTRUCK, dlc_name=dlc_name)
self.assertEqual(exp, obs)

def test_get_download_file_md5(self):
def test_get_download_file__info_md5(self):
api = Api()
api._Api__request = MagicMock()
api._Api__request.return_value = {"checksum": "url"}
@@ -150,10 +151,10 @@ def test_get_download_file_md5(self):
<chunk id="3" from="31457280" to="36717997" method="md5">0261b9225fc10c407df083f6d254c47b</chunk>
</file>'''
exp = "8acedf66c0d2986e7dee9af912b7df4f"
obs = api.get_download_file_md5("url")
obs = api.get_download_file_info("url").md5
self.assertEqual(exp, obs)

def test_get_download_file_md5_returns_empty_string_on_empty_response(self):
def test_get_download_file_info_md5_returns_empty_string_on_empty_response(self):
api = Api()
api._Api__request = MagicMock()
api._Api__request.return_value = {"checksum": "url"}
@@ -162,21 +163,21 @@ def test_get_download_file_md5_returns_empty_string_on_empty_response(self):
m_constants.SESSION.get().text = ""

exp = ""
obs = api.get_download_file_md5("url")
obs = api.get_download_file_info("url").md5
self.assertEqual(exp, obs)

def test_get_download_file_md5_returns_empty_string_on_response_error(self):
def test_get_download_file_info_md5_returns_empty_string_on_response_error(self):
api = Api()
api._Api__request = MagicMock()
api._Api__request.return_value = {"checksum": "url"}
m_constants.SESSION.get.side_effect = MagicMock()
m_constants.SESSION.get().status_code = http.HTTPStatus.NOT_FOUND

exp = ""
obs = api.get_download_file_md5("url")
obs = api.get_download_file_info("url").md5
self.assertEqual(exp, obs)

def test_get_download_file_md5_returns_empty_string_on_missing_md5(self):
def test_get_download_file_info_md5_returns_empty_string_on_missing_md5(self):
api = Api()
api._Api__request = MagicMock()
api._Api__request.return_value = {"checksum": "url"}
@@ -190,10 +191,10 @@ def test_get_download_file_md5_returns_empty_string_on_missing_md5(self):
</file>'''

exp = ""
obs = api.get_download_file_md5("url")
obs = api.get_download_file_info("url").md5
self.assertEqual(exp, obs)

def test_get_file_size(self):
def test_get_file_info_size(self):
api = Api()
api._Api__request = MagicMock()
api._Api__request.return_value = {"checksum": "url"}
@@ -206,10 +207,10 @@ def test_get_file_size(self):
<chunk id="3" from="31457280" to="36717997" method="md5">0261b9225fc10c407df083f6d254c47b</chunk>
</file>'''
exp = 36717998
obs = api.get_file_size("url")
obs = api.get_download_file_info("url").size
self.assertEqual(exp, obs)

def test_get_file_size_returns_zero_on_empty_response(self):
def test_get_file_info_size_returns_zero_on_empty_response(self):
api = Api()
api._Api__request = MagicMock()
api._Api__request.return_value = {"checksum": "url"}
@@ -218,21 +219,41 @@ def test_get_file_size_returns_zero_on_empty_response(self):
m_constants.SESSION.get().text = ""

exp = 0
obs = api.get_file_size("url")
obs = api.get_download_file_info("url").size
self.assertEqual(exp, obs)

def test_get_file_size_returns_zero_on_response_error(self):
def test_get_file_info_size_returns_zero_on_response_error(self):
api = Api()
api._Api__request = MagicMock()
api._Api__request.return_value = {"checksum": "url"}
m_constants.SESSION.get.side_effect = MagicMock()
m_constants.SESSION.get().status_code = http.HTTPStatus.NOT_FOUND

exp = 0
obs = api.get_file_size("url")
obs = api.get_download_file_info("url").size
self.assertEqual(exp, obs)

def test_get_file_info_size_returns_zero_on_request_exception(self):
api = Api()
api._Api__request = MagicMock()
api._Api__request.return_value = {"checksum": "url"}
m_constants.SESSION.get.side_effect = requests.exceptions.RequestException("test")

exp = 0
obs = api.get_download_file_info("url").size
self.assertEqual(exp, obs)

def test_get_file_size_returns_zero_on_missing_total_size(self):
def test_get_file_info_size_returns_zero_on_request_timeout_exception(self):
api = Api()
api._Api__request = MagicMock()
api._Api__request.return_value = {"checksum": "url"}
m_constants.SESSION.get.side_effect = requests.exceptions.ReadTimeout("test")

exp = 0
obs = api.get_download_file_info("url").size
self.assertEqual(exp, obs)

def test_get_file_info_size_returns_zero_on_missing_total_size(self):
api = Api()
api._Api__request = MagicMock()
api._Api__request.return_value = {"checksum": "url"}
@@ -246,7 +267,7 @@ def test_get_file_size_returns_zero_on_missing_total_size(self):
</file>'''

exp = 0
obs = api.get_file_size("url")
obs = api.get_download_file_info("url").size
self.assertEqual(exp, obs)

def test1_get_gamesdb_info(self):
@@ -281,6 +302,38 @@ def test3_get_gamesdb_info_no_genre(self):
obs = api.get_gamesdb_info(test_game)
self.assertEqual(exp, obs)

def test_get_user_info_from_api(self):
username = "test"
api = Api()
api._Api__request = MagicMock()
api._Api__request.return_value = {"username": username}
m_config.Config.get.return_value = ""
m_constants.SESSION.get.side_effect = MagicMock()
m_constants.SESSION.get().status_code = http.HTTPStatus.OK

obs = api.get_user_info()
self.assertEqual(username, obs)

def test_get_user_info_from_config(self):
username = "test"
api = Api()
api._Api__request = MagicMock()
api._Api__request.return_value = {"username": "wrong"}
m_config.Config.get.return_value = username

obs = api.get_user_info()
self.assertEqual(username, obs)

def test_get_user_info_return_empty_string_when_nothing_is_returned(self):
api = Api()
api._Api__request = MagicMock()
api._Api__request.return_value = {}
m_config.Config.get.return_value = ""

exp = ""
obs = api.get_user_info()
self.assertEqual(exp, obs)


del sys.modules['minigalaxy.constants']
del sys.modules['minigalaxy.config']
83 changes: 82 additions & 1 deletion tests/test_launcher.py
Original file line number Diff line number Diff line change
@@ -104,7 +104,88 @@ def test2_get_windows_exe_cmd(self, mock_os_chdir, mo):
files = ['thumbnail.jpg', 'docs', 'support', 'game', 'minigalaxy-dlc.json', 'MetroExodus.exe', 'unins000.exe',
'goggame-1407287452.info', 'goggame-1414471894.info']
game = Game("Test Game", install_dir="/test/install/dir")
exp = ["wine", "MetroExodus.exe"]
exp = ['wine', 'start', '/b', '/wait', '/d', '.', 'MetroExodus.exe']
obs = launcher.get_windows_exe_cmd(game, files)
self.assertEqual(exp, obs)

@mock.patch('builtins.open', new_callable=mock_open, read_data="")
@mock.patch('os.chdir')
def test3_get_windows_exe_cmd(self, mock_os_chdir, mo):
goggame_1207658919_info_content = """{
"buildId": "52095557858882770",
"clientId": "49843178982252086",
"gameId": "1207658919",
"language": "English",
"languages": [
"en-US"
],
"name": "Rayman Forever",
"playTasks": [
{
"arguments": "-conf \\"..\\\\dosboxRayman.conf\\" -conf \\"..\\\\dosboxRayman_single.conf\\" -noconsole -c \\"exit\\"",
"category": "game",
"isPrimary": true,
"languages": [
"*"
],
"name": "Rayman Forever",
"path": "DOSBOX\\\\dosbox.exe",
"type": "FileTask",
"workingDir": "DOSBOX"
},
{
"arguments": "1207658919",
"category": "tool",
"languages": [
"*"
],
"name": "Graphic Mode Setup",
"path": "DOSBOX\\\\GOGDOSConfig.exe",
"type": "FileTask",
"workingDir": "DOSBOX"
},
{
"category": "document",
"languages": [
"*"
],
"link": "http://www.gog.com/support/rayman_forever",
"name": "Support",
"type": "URLTask"
},
{
"category": "document",
"languages": [
"*"
],
"name": "Manual",
"path": "Manual.pdf",
"type": "FileTask"
},
{
"category": "tool",
"languages": [
"*"
],
"name": "Mapper",
"path": "RayKit\\\\Mapper.exe",
"type": "FileTask",
"workingDir": "RayKit"
}
],
"rootGameId": "1207658919",
"version": 1
}"""
mo.side_effect = (mock_open(read_data=goggame_1207658919_info_content).return_value,)
files = ['goggame-1207658919.script', 'DOSBOX', 'thumbnail.jpg', 'game.gog', 'unins000.dat', 'webcache.zip',
'EULA.txt', 'Music', 'dosboxRayman_single.conf', 'Rayman', 'unins000.exe', 'support.ico', 'prefix',
'goggame-1207658919.info', 'Manual.pdf', 'gog.ico', 'unins000.msg', 'goggame-1207658919.hashdb',
'RayFan', 'dosboxRayman.conf', 'unins000.ini', 'thumbnail_100.jpg', 'RayKit', 'game.ins',
'goggame-1207658919.ico', 'goglog.ini', 'Launch Rayman Forever.lnk', 'cloud_saves',
'thumbnail_196.jpg']
game = Game("Test Game", install_dir="/test/install/dir")
exp = ['wine', 'start', '/b', '/wait', '/d', 'DOSBOX', 'DOSBOX\\dosbox.exe', '-conf', '"..\\dosboxRayman.conf"',
'-conf', '"..\\dosboxRayman_single.conf"', '-noconsole', '-c', '"exit"']
obs = launcher.get_windows_exe_cmd(game, files)
self.assertEqual(exp, obs)

0 comments on commit a3aa41b

Please sign in to comment.