mirror of
https://github.com/aclist/dztui.git
synced 2026-10-31 07:02:50 +01:00
Compare commits
35
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78144e9127 | ||
|
|
33b8dfba70 | ||
|
|
abf0060cac | ||
|
|
95daa9163b | ||
|
|
ff068e5396 | ||
|
|
46117ecdda | ||
|
|
30119fec46 | ||
|
|
ac01818549 | ||
|
|
d486712a4c | ||
|
|
d116702145 | ||
|
|
5bee3614bd | ||
|
|
c73b07991f | ||
|
|
199cd07982 | ||
|
|
e3d62c6749 | ||
|
|
5134bf02fb | ||
|
|
f97be9352d | ||
|
|
b092a809f4 | ||
|
|
2cc7710807 | ||
|
|
5fb10d9758 | ||
|
|
100cb43afc | ||
|
|
7e2a585812 | ||
|
|
c1181435b3 | ||
|
|
22a7814ce4 | ||
|
|
b030da5c65 | ||
|
|
3c0acdd068 | ||
|
|
25ae2052e4 | ||
|
|
1b1777117f | ||
|
|
3e6bd95285 | ||
|
|
f4be9a31e9 | ||
|
|
a6282e4401 | ||
|
|
a9ae5f59b5 | ||
|
|
97ee2b7307 | ||
|
|
ff6edb6181 | ||
|
|
4c22263fb5 | ||
|
|
141f96fb9f |
No files matched your search
@@ -17,7 +17,7 @@ DZGUI follows a specific set of guidelines, enumerated below:
|
|||||||
- Provide a free and open-source tool under GPL
|
- Provide a free and open-source tool under GPL
|
||||||
- Do not use undocumented, third-party servers to aggregate data; no central "DZGUI" server
|
- Do not use undocumented, third-party servers to aggregate data; no central "DZGUI" server
|
||||||
- All connections go to Steam and DayZ servers directly; the tool should function in perpetuity without intermediary endpoints
|
- All connections go to Steam and DayZ servers directly; the tool should function in perpetuity without intermediary endpoints
|
||||||
- Target Linux desktop and Steam deck specifically; not a cross-platform tool
|
- Target Linux desktop and Steam Deck specifically; not a cross-platform tool
|
||||||
|
|
||||||
## Setup and usage
|
## Setup and usage
|
||||||
|
|
||||||
@@ -35,6 +35,8 @@ Geolocation records from [DB-IP](https://db-ip.com) under [CC 4.0 license](https
|
|||||||
Executable versions of DZGUI published as release binaries ship with various runtime dependencies and the Python interpreter built in.
|
Executable versions of DZGUI published as release binaries ship with various runtime dependencies and the Python interpreter built in.
|
||||||
Users wishing to review the licenses to these components can inspect the `LICENSE` file located in the release tarball.
|
Users wishing to review the licenses to these components can inspect the `LICENSE` file located in the release tarball.
|
||||||
|
|
||||||
|
Steam and the Steam logo are trademarks and/or registered trademarks of Valve Corporation in the U.S. and/or other countries.
|
||||||
|
|
||||||
## Disclaimer
|
## Disclaimer
|
||||||
|
|
||||||
DZGUI is beta-quality software and is provided as-is.
|
DZGUI is beta-quality software and is provided as-is.
|
||||||
@@ -173,11 +173,12 @@ class Shortcuts:
|
|||||||
self.save_shortcuts()
|
self.save_shortcuts()
|
||||||
|
|
||||||
def save_shortcuts(self) -> None:
|
def save_shortcuts(self) -> None:
|
||||||
try:
|
if self.shortcuts_path.is_file():
|
||||||
backup = self.shortcuts_path.with_suffix(".vdf.bak")
|
try:
|
||||||
shutil.copy(self.shortcuts_path, backup)
|
backup = self.shortcuts_path.with_suffix(".vdf.bak")
|
||||||
except Exception as e:
|
shutil.copy(self.shortcuts_path, backup)
|
||||||
logger.critical(e)
|
except Exception as e:
|
||||||
|
logger.critical(e)
|
||||||
try:
|
try:
|
||||||
with open(self.shortcuts_path, "wb") as f:
|
with open(self.shortcuts_path, "wb") as f:
|
||||||
vdf.binary_dump(self.shortcuts, f)
|
vdf.binary_dump(self.shortcuts, f)
|
||||||
|
|||||||
+5
-4
@@ -13,7 +13,7 @@ from warnings import deprecated
|
|||||||
|
|
||||||
from dzgui.api.acf import ACF
|
from dzgui.api.acf import ACF
|
||||||
from dzgui.init.prereqs import has_any_client
|
from dzgui.init.prereqs import has_any_client
|
||||||
from dzgui.init.proc import has_cmd
|
from dzgui.init.prereqs import has_flatpak_client, has_steam_client
|
||||||
from dzgui.const.constants import (
|
from dzgui.const.constants import (
|
||||||
APPID_DAYZ,
|
APPID_DAYZ,
|
||||||
APPID_DAYZ_EXP,
|
APPID_DAYZ_EXP,
|
||||||
@@ -23,11 +23,10 @@ from dzgui.const.constants import (
|
|||||||
DEBIAN_STEAM_PATH,
|
DEBIAN_STEAM_PATH,
|
||||||
DEFAULT_STEAM_PATH,
|
DEFAULT_STEAM_PATH,
|
||||||
DOWNLOADS_PATH,
|
DOWNLOADS_PATH,
|
||||||
FLATPAK_CMD,
|
|
||||||
FLATPAK_STEAM_PATH,
|
FLATPAK_STEAM_PATH,
|
||||||
LIBRARYFOLDERS_PATH,
|
LIBRARYFOLDERS_PATH,
|
||||||
UBUNTU_STEAM_PATH,
|
|
||||||
REQUEST_TIMEOUT,
|
REQUEST_TIMEOUT,
|
||||||
|
UBUNTU_STEAM_PATH,
|
||||||
VDF_PATH,
|
VDF_PATH,
|
||||||
)
|
)
|
||||||
from dzgui.const.endpoints import (
|
from dzgui.const.endpoints import (
|
||||||
@@ -84,7 +83,9 @@ def get_steam_paths() -> list[tuple[Path, str]]:
|
|||||||
}
|
}
|
||||||
|
|
||||||
for path in DEFAULT_PATH, FLATPAK_PATH, UBUNTU_PATH, DEBIAN_PATH:
|
for path in DEFAULT_PATH, FLATPAK_PATH, UBUNTU_PATH, DEBIAN_PATH:
|
||||||
if path is FLATPAK_PATH and has_cmd(FLATPAK_CMD) is False:
|
if path is FLATPAK_PATH and has_flatpak_client() is False:
|
||||||
|
continue
|
||||||
|
if path is not FLATPAK_PATH and has_steam_client() is False:
|
||||||
continue
|
continue
|
||||||
if path.joinpath(VDF_PATH).is_file():
|
if path.joinpath(VDF_PATH).is_file():
|
||||||
paths.append((path, human[path]))
|
paths.append((path, human[path]))
|
||||||
|
|||||||
+21
-16
@@ -2,12 +2,17 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
|
from importlib import resources
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from warnings import deprecated
|
|
||||||
|
|
||||||
|
|
||||||
from dzgui.const.constants import APP_NAME, STEAM_ICON, STEAM_ICONS_PATH
|
from dzgui.const.constants import (
|
||||||
|
APP_NAME,
|
||||||
|
APP_NAME_LOWER,
|
||||||
|
FLATPAK_ROOT_PATH,
|
||||||
|
ICONS_PATH,
|
||||||
|
)
|
||||||
from dzgui.const.enum import Preferences
|
from dzgui.const.enum import Preferences
|
||||||
from dzgui.config.query import lookup
|
from dzgui.config.query import lookup
|
||||||
from dzgui.config.userprefs import UserPrefs
|
from dzgui.config.userprefs import UserPrefs
|
||||||
@@ -18,7 +23,7 @@ from dzgui.init.migrate import (
|
|||||||
migrate_cols_file,
|
migrate_cols_file,
|
||||||
)
|
)
|
||||||
from dzgui.init.flock import lock_acquire
|
from dzgui.init.flock import lock_acquire
|
||||||
from dzgui.init.prereqs import has_any_client
|
from dzgui.init.prereqs import has_flatpak_client, has_steam_client
|
||||||
from dzgui.strings import boot
|
from dzgui.strings import boot
|
||||||
|
|
||||||
from dzgui.util.deck import is_steam_deck, is_game_mode
|
from dzgui.util.deck import is_steam_deck, is_game_mode
|
||||||
@@ -26,7 +31,7 @@ from dzgui.util.dirs import make_parents
|
|||||||
from dzgui.util.localize import set_locale
|
from dzgui.util.localize import set_locale
|
||||||
from dzgui.util.map_count import test_map_count
|
from dzgui.util.map_count import test_map_count
|
||||||
from dzgui.util.redact import RedactionFilter, REDACTION_PATTERNS
|
from dzgui.util.redact import RedactionFilter, REDACTION_PATTERNS
|
||||||
from dzgui.util.strings import init, steam_icon_missing
|
from dzgui.util.strings import init
|
||||||
|
|
||||||
from dzgui.views.base import App
|
from dzgui.views.base import App
|
||||||
from dzgui.views.dialogs.boot import BootWindow
|
from dzgui.views.dialogs.boot import BootWindow
|
||||||
@@ -99,26 +104,21 @@ def copy_bare_configs(config: "Path", resolution: "Path") -> tuple[bool, bool]:
|
|||||||
return (config_changed, state_changed)
|
return (config_changed, state_changed)
|
||||||
|
|
||||||
|
|
||||||
@deprecated("Currently unused")
|
def load_steam_icon() -> None:
|
||||||
def load_steam_icon(config: Path) -> None:
|
|
||||||
import gi
|
import gi
|
||||||
|
|
||||||
gi.require_version("Gtk", "3.0")
|
gi.require_version("Gtk", "3.0")
|
||||||
from gi.repository import Gtk # noqa
|
from gi.repository import Gtk # noqa
|
||||||
|
|
||||||
default = lookup(config, Preferences.DEFAULT)
|
|
||||||
icon_path = Path(default).joinpath(STEAM_ICONS_PATH)
|
|
||||||
try:
|
try:
|
||||||
|
traversable = resources.files(APP_NAME_LOWER).joinpath(ICONS_PATH)
|
||||||
|
icons = Path(str(traversable))
|
||||||
|
|
||||||
theme = Gtk.IconTheme.get_default()
|
theme = Gtk.IconTheme.get_default()
|
||||||
paths = theme.get_search_path()
|
paths = theme.get_search_path()
|
||||||
theme.set_search_path(paths + [str(icon_path)])
|
theme.set_search_path(paths + [str(icons)])
|
||||||
paths = theme.get_search_path()
|
|
||||||
icons = theme.list_icons(None)
|
|
||||||
if STEAM_ICON not in icons:
|
|
||||||
logger.warning(steam_icon_missing)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
logger.critical(e)
|
||||||
logger.warning(e)
|
|
||||||
|
|
||||||
|
|
||||||
def load_gui(version: str, is_debug: bool) -> None:
|
def load_gui(version: str, is_debug: bool) -> None:
|
||||||
@@ -161,7 +161,11 @@ def load_gui(version: str, is_debug: bool) -> None:
|
|||||||
# TODO: sudo escalation dialog
|
# TODO: sudo escalation dialog
|
||||||
# count = get_map_count()
|
# count = get_map_count()
|
||||||
# TODO: move into module
|
# TODO: move into module
|
||||||
if has_any_client() is False:
|
if FLATPAK_ROOT_PATH in lookup(XDG.config, Preferences.DEFAULT):
|
||||||
|
has_client = has_flatpak_client()
|
||||||
|
else:
|
||||||
|
has_client = has_steam_client()
|
||||||
|
if has_client is False:
|
||||||
EarlyAlertDialog(init.requires_steam)
|
EarlyAlertDialog(init.requires_steam)
|
||||||
|
|
||||||
bootwin = BootWindow(XDG, version)
|
bootwin = BootWindow(XDG, version)
|
||||||
@@ -180,5 +184,6 @@ def load_gui(version: str, is_debug: bool) -> None:
|
|||||||
use_miles=use_miles,
|
use_miles=use_miles,
|
||||||
)
|
)
|
||||||
print(boot.all_ok)
|
print(boot.all_ok)
|
||||||
|
load_steam_icon()
|
||||||
app = App(prefs)
|
app = App(prefs)
|
||||||
app.run(None)
|
app.run(None)
|
||||||
+102
-3
@@ -3,6 +3,9 @@ import logging
|
|||||||
import re
|
import re
|
||||||
import requests
|
import requests
|
||||||
import shutil
|
import shutil
|
||||||
|
import socket
|
||||||
|
import sqlite3
|
||||||
|
import time
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -22,15 +25,16 @@ def find_download_url(url: str) -> str | None:
|
|||||||
return str(urls[0])
|
return str(urls[0])
|
||||||
|
|
||||||
|
|
||||||
def get_ipdb(ips_path: Path) -> None:
|
def get_ipdb(db_path: Path) -> None:
|
||||||
try:
|
try:
|
||||||
url = find_download_url(DB_IP)
|
url = find_download_url(DB_IP)
|
||||||
if url is None:
|
if url is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
ips_path = db_path.with_suffix(".csv")
|
||||||
|
month_file = db_path.parent / ".month"
|
||||||
date = find_date(url)
|
date = find_date(url)
|
||||||
month_file = ips_path.parent / ".month"
|
if db_path.exists() and month_file.exists():
|
||||||
if ips_path.exists() and month_file.exists():
|
|
||||||
old_date = month_file.read_text().rstrip("\n")
|
old_date = month_file.read_text().rstrip("\n")
|
||||||
if old_date == date:
|
if old_date == date:
|
||||||
logger.info(f"IP DB date matches: {date}")
|
logger.info(f"IP DB date matches: {date}")
|
||||||
@@ -45,6 +49,9 @@ def get_ipdb(ips_path: Path) -> None:
|
|||||||
logger.info("Stripping IPv6 records")
|
logger.info("Stripping IPv6 records")
|
||||||
strip_ipv6(ips_path)
|
strip_ipv6(ips_path)
|
||||||
|
|
||||||
|
create_table(db_path, ips_path)
|
||||||
|
ips_path.unlink()
|
||||||
|
|
||||||
with open(month_file, "w") as f:
|
with open(month_file, "w") as f:
|
||||||
f.write(date)
|
f.write(date)
|
||||||
logger.info(f"Wrote {date} to {month_file}")
|
logger.info(f"Wrote {date} to {month_file}")
|
||||||
@@ -108,3 +115,95 @@ def strip_ipv6(path: Path) -> None:
|
|||||||
out.write(merged)
|
out.write(merged)
|
||||||
path.unlink()
|
path.unlink()
|
||||||
alt_path.rename(path)
|
alt_path.rename(path)
|
||||||
|
|
||||||
|
|
||||||
|
def ip_to_int(ip: str) -> int:
|
||||||
|
return int.from_bytes(socket.inet_aton(ip), "big")
|
||||||
|
|
||||||
|
|
||||||
|
def marshal_rows(ip_file: Path) -> list[tuple[int, int, str, str]]:
|
||||||
|
YIELD = 0.003
|
||||||
|
SLEEP = 0.001
|
||||||
|
with open(ip_file, "r") as f:
|
||||||
|
rows: list[tuple[int, int, str, str]] = []
|
||||||
|
# NOTE: yield to GIL periodically to reduce load on spinner
|
||||||
|
last_yield = time.monotonic()
|
||||||
|
for row in f:
|
||||||
|
start, end, lat, lon = row.rstrip("\n").split(",")
|
||||||
|
int_start = ip_to_int(start)
|
||||||
|
int_end = ip_to_int(end)
|
||||||
|
rows.append((int_start, int_end, lat, lon))
|
||||||
|
|
||||||
|
now = time.monotonic()
|
||||||
|
if (now - last_yield) >= YIELD:
|
||||||
|
time.sleep(SLEEP)
|
||||||
|
last_yield = time.monotonic()
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
|
def create_table(db_file: Path, ip_file: Path) -> None:
|
||||||
|
create = """
|
||||||
|
CREATE TABLE IF NOT EXISTS ips (
|
||||||
|
start_range INTEGER PRIMARY KEY,
|
||||||
|
end_range INTEGER,
|
||||||
|
latitude REAL,
|
||||||
|
longitude REAL
|
||||||
|
) WITHOUT ROWID;
|
||||||
|
"""
|
||||||
|
insert = """
|
||||||
|
INSERT INTO ips
|
||||||
|
(start_range, end_range, latitude, longitude)
|
||||||
|
values(?, ?, ?, ?)
|
||||||
|
"""
|
||||||
|
|
||||||
|
if db_file.exists():
|
||||||
|
db_file.unlink()
|
||||||
|
|
||||||
|
conn = sqlite3.connect(":memory:")
|
||||||
|
c = conn.cursor()
|
||||||
|
|
||||||
|
try:
|
||||||
|
c.execute(create)
|
||||||
|
rows = marshal_rows(ip_file)
|
||||||
|
c.executemany(insert, rows)
|
||||||
|
logger.info(f"Inserted {len(rows)} IP records into database")
|
||||||
|
conn.commit()
|
||||||
|
with sqlite3.connect(db_file) as dest:
|
||||||
|
conn.backup(dest)
|
||||||
|
logger.info(f"Serialized IP records to database: '{db_file}'")
|
||||||
|
conn.close()
|
||||||
|
except Exception as e:
|
||||||
|
logger.critical(e)
|
||||||
|
|
||||||
|
|
||||||
|
def read_from_table(db_path: Path, ip: str) -> tuple[float, float]:
|
||||||
|
if not db_path.exists():
|
||||||
|
raise FileNotFoundError(f"No file found at the location '{db_path}")
|
||||||
|
|
||||||
|
conn = sqlite3.connect(db_path)
|
||||||
|
c = conn.cursor()
|
||||||
|
|
||||||
|
int_ip = ip_to_int(ip)
|
||||||
|
|
||||||
|
query = """
|
||||||
|
SELECT end_range, latitude, longitude
|
||||||
|
FROM ips
|
||||||
|
WHERE start_range <= ?
|
||||||
|
ORDER BY start_range DESC
|
||||||
|
LIMIT 1
|
||||||
|
"""
|
||||||
|
index = """
|
||||||
|
CREATE INDEX IF NOT EXISTS start_index
|
||||||
|
ON ips(start_range);
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
c.execute(index)
|
||||||
|
conn.commit()
|
||||||
|
c.execute(query, (int_ip,))
|
||||||
|
row = c.fetchone()
|
||||||
|
end, lat, lon = row
|
||||||
|
conn.close()
|
||||||
|
return lat, lon
|
||||||
|
except Exception as e:
|
||||||
|
logger.critical(e)
|
||||||
|
raise e
|
||||||
+3
-3
@@ -22,7 +22,7 @@ class Xdg:
|
|||||||
version: Path
|
version: Path
|
||||||
system: Path
|
system: Path
|
||||||
debug: Path
|
debug: Path
|
||||||
ips: Path
|
db: Path
|
||||||
filters: Path
|
filters: Path
|
||||||
shortcut: Path
|
shortcut: Path
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ def parse_filepaths(xdg: dict) -> Xdg:
|
|||||||
resolution = state / "dzg.res.json"
|
resolution = state / "dzg.res.json"
|
||||||
history = state / "dzg.history"
|
history = state / "dzg.history"
|
||||||
versions = state / "dzg.versions"
|
versions = state / "dzg.versions"
|
||||||
ips = state / "ips.csv"
|
db = state / "ips.db"
|
||||||
filters = state / "dzg.filters.json"
|
filters = state / "dzg.filters.json"
|
||||||
|
|
||||||
shortcut = share / APP_NAME_LOWER
|
shortcut = share / APP_NAME_LOWER
|
||||||
@@ -102,7 +102,7 @@ def parse_filepaths(xdg: dict) -> Xdg:
|
|||||||
versions,
|
versions,
|
||||||
system,
|
system,
|
||||||
debug,
|
debug,
|
||||||
ips,
|
db,
|
||||||
filters,
|
filters,
|
||||||
shortcut,
|
shortcut,
|
||||||
)
|
)
|
||||||
@@ -51,7 +51,7 @@ INPUT_KEYBOARD = "input-keyboard-symbolic"
|
|||||||
LIST_ADD = "list-add-symbolic"
|
LIST_ADD = "list-add-symbolic"
|
||||||
REFRESH_ICON = "view-refresh-symbolic"
|
REFRESH_ICON = "view-refresh-symbolic"
|
||||||
SEARCH_ICON = "system-search-symbolic"
|
SEARCH_ICON = "system-search-symbolic"
|
||||||
STEAM_ICON = "steam_tray_mono"
|
STEAM_ICON = "steam-tray-symbolic"
|
||||||
VIEW_CONCEAL = "view-conceal-symbolic"
|
VIEW_CONCEAL = "view-conceal-symbolic"
|
||||||
VIEW_REVEAL = "view-reveal-symbolic"
|
VIEW_REVEAL = "view-reveal-symbolic"
|
||||||
WARNING = "dialog-warning-symbolic"
|
WARNING = "dialog-warning-symbolic"
|
||||||
@@ -80,6 +80,7 @@ DEBUG_LOG = f"{APP_NAME}_DEBUG.LOG"
|
|||||||
SYSTEM_LOG = f"{APP_NAME}_SYSTEM.LOG"
|
SYSTEM_LOG = f"{APP_NAME}_SYSTEM.LOG"
|
||||||
CHANGELOG_PATH = "data/CHANGELOG.md"
|
CHANGELOG_PATH = "data/CHANGELOG.md"
|
||||||
IMAGES_PATH = "data/images"
|
IMAGES_PATH = "data/images"
|
||||||
|
ICONS_PATH = "data/images/gtk"
|
||||||
|
|
||||||
HERO_PATH = "data/images/_hero.png"
|
HERO_PATH = "data/images/_hero.png"
|
||||||
CSS_PATH = "data/app.css"
|
CSS_PATH = "data/app.css"
|
||||||
@@ -89,13 +90,13 @@ FLATPAK_ROOT_PATH = ".var/app/com.valvesoftware.Steam"
|
|||||||
FLATPAK_STEAM_PATH = FLATPAK_ROOT_PATH + "/data/Steam"
|
FLATPAK_STEAM_PATH = FLATPAK_ROOT_PATH + "/data/Steam"
|
||||||
UBUNTU_STEAM_PATH = ".steam/steam"
|
UBUNTU_STEAM_PATH = ".steam/steam"
|
||||||
DEBIAN_STEAM_PATH = ".steam/debian-installation"
|
DEBIAN_STEAM_PATH = ".steam/debian-installation"
|
||||||
STEAM_ICONS_PATH = "public"
|
|
||||||
|
|
||||||
LOG_FILTERS = ("CRITICAL", "WARNING", "INFO", "DEBUG")
|
LOG_FILTERS = ("CRITICAL", "WARNING", "INFO", "DEBUG")
|
||||||
|
|
||||||
TMP_PATH = "/tmp"
|
TMP_PATH = "/tmp"
|
||||||
TMP_TARBALL = "/tmp/dzgui.tar.gz"
|
TMP_TARBALL = "/tmp/dzgui.tar.gz"
|
||||||
TMP_EXE = "/tmp/dzgui/dzgui"
|
TMP_EXE = "/tmp/dzgui/dzgui"
|
||||||
|
LOCK_FILE = "/tmp/dzgui.flock"
|
||||||
|
|
||||||
DAYZ_COMMUNITY_ROOT = "Missions"
|
DAYZ_COMMUNITY_ROOT = "Missions"
|
||||||
|
|
||||||
|
|||||||
@@ -111,6 +111,10 @@ class Emitter(GObject.GObject):
|
|||||||
def check_toggled(self, label: str, state: bool) -> None:
|
def check_toggled(self, label: str, state: bool) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@GObject.Signal(flags=GObject.SignalFlags.RUN_LAST, arg_types=())
|
||||||
|
def servers_timed_out(self) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
@GObject.Signal(flags=GObject.SignalFlags.RUN_LAST, arg_types=(object,))
|
@GObject.Signal(flags=GObject.SignalFlags.RUN_LAST, arg_types=(object,))
|
||||||
def servers_loaded(self, tag: "ServerTab") -> None:
|
def servers_loaded(self, tag: "ServerTab") -> None:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.3.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d="M568 320C568 457 456.8 568 319.6 568C205.8 568 110 491.7 80.6 387.6L175.8 426.9C182.2 459 210.7 483.3 244.7 483.3C283.9 483.3 316.6 450.9 314.9 409.8L399.4 349.6C451.5 350.9 495.2 308.7 495.2 256.1C495.2 204.5 453.2 162.6 401.5 162.6C349.8 162.6 307.8 204.6 307.8 256.1L307.8 257.3L248.6 343C233.1 342.1 217.9 346.4 205.1 355.1L72 300.1C82.2 172.4 189.1 72 319.6 72C456.8 72 568 183 568 320zM227.7 448.3L197.2 435.7C202.8 447.3 212.5 456.5 224.4 461.5C251.3 472.7 282.2 459.9 293.4 433.1C298.8 420.1 298.9 405.8 293.5 392.8C288.1 379.8 278 369.6 265 364.2C252.1 358.8 238.3 359 226.1 363.6L257.6 376.6C277.4 384.8 286.8 407.5 278.5 427.3C270.2 447.2 247.5 456.5 227.7 448.3zM401.5 193.8C435.9 193.8 463.8 221.7 463.8 256.1C463.8 290.5 435.9 318.4 401.5 318.4C367.1 318.4 339.2 290.5 339.2 256.1C339.2 221.7 367.1 193.8 401.5 193.8zM401.6 302.8C427.4 302.8 448.4 281.8 448.4 256C448.4 230.2 427.4 209.2 401.6 209.2C375.8 209.2 354.8 230.2 354.8 256C354.8 281.8 375.8 302.8 401.6 302.8z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -10,10 +10,10 @@ if TYPE_CHECKING:
|
|||||||
logger = logging.getLogger(APP_NAME)
|
logger = logging.getLogger(APP_NAME)
|
||||||
|
|
||||||
|
|
||||||
def get_local_coords(path: "Path") -> Coords | None:
|
def get_local_coords(db_path: "Path") -> Coords | None:
|
||||||
try:
|
try:
|
||||||
my_ip = get_local_ip()
|
my_ip = get_local_ip()
|
||||||
return get_coords(path, my_ip)
|
return get_coords(db_path, my_ip)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warn(e)
|
logger.warn(e)
|
||||||
return None
|
return None
|
||||||
+3
-2
@@ -5,8 +5,9 @@ import sys
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TextIO
|
from typing import TextIO
|
||||||
|
|
||||||
from dzgui.views.dialogs.early_alert import EarlyAlertDialog
|
from dzgui.const.constants import LOCK_FILE
|
||||||
from dzgui.util.strings import cannot_acquire_lock
|
from dzgui.util.strings import cannot_acquire_lock
|
||||||
|
from dzgui.views.dialogs.early_alert import EarlyAlertDialog
|
||||||
|
|
||||||
|
|
||||||
def lock_release(lock: TextIO) -> None:
|
def lock_release(lock: TextIO) -> None:
|
||||||
@@ -15,7 +16,7 @@ def lock_release(lock: TextIO) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def lock_acquire() -> TextIO:
|
def lock_acquire() -> TextIO:
|
||||||
lockfile = Path("/tmp/dzgui.flock")
|
lockfile = Path(LOCK_FILE)
|
||||||
lock = open(lockfile, "w")
|
lock = open(lockfile, "w")
|
||||||
try:
|
try:
|
||||||
fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||||
|
|||||||
@@ -3,11 +3,14 @@ import shutil
|
|||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from dzgui.const.constants import APP_NAME, FLATPAK_APPID, FLATPAK_CMD, STEAM_CMD
|
from dzgui.const.constants import APP_NAME, FLATPAK_APPID, FLATPAK_CMD, STEAM_CMD
|
||||||
|
from dzgui.init.proc import is_sandboxed
|
||||||
|
|
||||||
logger = logging.getLogger(APP_NAME)
|
logger = logging.getLogger(APP_NAME)
|
||||||
|
|
||||||
|
|
||||||
def has_flatpak_client() -> bool:
|
def has_flatpak_client() -> bool:
|
||||||
|
if is_sandboxed():
|
||||||
|
return True
|
||||||
try:
|
try:
|
||||||
res = subprocess.run(
|
res = subprocess.run(
|
||||||
[FLATPAK_CMD, "list", "--columns=application"],
|
[FLATPAK_CMD, "list", "--columns=application"],
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ class ServerModelManager:
|
|||||||
job = Servers.query_api
|
job = Servers.query_api
|
||||||
params = Servers.params
|
params = Servers.params
|
||||||
servers = []
|
servers = []
|
||||||
|
|
||||||
with ThreadPoolExecutor() as executor:
|
with ThreadPoolExecutor() as executor:
|
||||||
futures = [executor.submit(job, key, APPID_DAYZ, param) for param in params]
|
futures = [executor.submit(job, key, APPID_DAYZ, param) for param in params]
|
||||||
for future in as_completed(futures):
|
for future in as_completed(futures):
|
||||||
@@ -414,6 +415,8 @@ class ServerModelManager:
|
|||||||
# TODO: disable map, keyword, and filter widgets if model is None
|
# TODO: disable map, keyword, and filter widgets if model is None
|
||||||
# signal driven (servers_empty, servers_failed_to_load)
|
# signal driven (servers_empty, servers_failed_to_load)
|
||||||
|
|
||||||
|
self.emitter.emit("servers_timed_out")
|
||||||
|
|
||||||
if self.preserve_on_fail is False:
|
if self.preserve_on_fail is False:
|
||||||
self.tv.set_model(None)
|
self.tv.set_model(None)
|
||||||
filter_man = self.tv.get_filter_man()
|
filter_man = self.tv.get_filter_man()
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ desc_flatpak_path = (
|
|||||||
desc_ubuntu_path = "This is the default Steam path on <b>Ubuntu-based systems</b>."
|
desc_ubuntu_path = "This is the default Steam path on <b>Ubuntu-based systems</b>."
|
||||||
desc_debian_path = "This is the default Steam path on <b>Debian-based systems</b>."
|
desc_debian_path = "This is the default Steam path on <b>Debian-based systems</b>."
|
||||||
no_valid_paths = (
|
no_valid_paths = (
|
||||||
"No valid Steam paths found on system. Please install Steam to continue."
|
"No valid Steam path or Steam command found on system. Please install Steam to continue."
|
||||||
)
|
)
|
||||||
button_scan = "Scan for Steam"
|
button_scan = "Scan for Steam"
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -80,8 +80,9 @@ class CalcDist:
|
|||||||
if local is None:
|
if local is None:
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
remote = get_coords(prefs.paths.ips, remote_ip)
|
remote = get_coords(prefs.paths.db, remote_ip)
|
||||||
except Exception:
|
except Exception as e:
|
||||||
|
logger.critical(e)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
haversine = Haversine(local.lat, local.lon, remote.lat, remote.lon)
|
haversine = Haversine(local.lat, local.lon, remote.lat, remote.lon)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from dzgui.const.constants import APP_NAME, FLATPAK_APPID, GTK_APP_ID
|
from dzgui.const.constants import APP_NAME, FLATPAK_APPID, GTK_APP_ID, TMP_PATH
|
||||||
|
|
||||||
logger = logging.getLogger(APP_NAME)
|
logger = logging.getLogger(APP_NAME)
|
||||||
|
|
||||||
@@ -18,7 +18,11 @@ def apply_overrides(xdg: dict[str, str]) -> None:
|
|||||||
]
|
]
|
||||||
subprocess.run([*args], capture_output=True)
|
subprocess.run([*args], capture_output=True)
|
||||||
|
|
||||||
for path in xdg.values():
|
paths: list[str] = []
|
||||||
|
paths.extend([*xdg.values()])
|
||||||
|
paths.append(TMP_PATH)
|
||||||
|
|
||||||
|
for path in paths:
|
||||||
args = [
|
args = [
|
||||||
"flatpak",
|
"flatpak",
|
||||||
"--user",
|
"--user",
|
||||||
|
|||||||
+14
-45
@@ -1,4 +1,5 @@
|
|||||||
import a2s
|
import a2s
|
||||||
|
from ipaddress import ip_address
|
||||||
import requests
|
import requests
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -7,6 +8,7 @@ from dataclasses import dataclass
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from dzgui.api.servers import Record
|
from dzgui.api.servers import Record
|
||||||
|
from dzgui.config.ipdb import read_from_table
|
||||||
from dzgui.const.constants import REQUEST_TIMEOUT
|
from dzgui.const.constants import REQUEST_TIMEOUT
|
||||||
from dzgui.const.endpoints import IP_ECHO
|
from dzgui.const.endpoints import IP_ECHO
|
||||||
|
|
||||||
@@ -33,54 +35,21 @@ def ip2list(ip: str) -> list:
|
|||||||
return [int(digit) for digit in ip.split(".")]
|
return [int(digit) for digit in ip.split(".")]
|
||||||
|
|
||||||
|
|
||||||
# TODO: tests
|
def is_usable(ip: str) -> bool:
|
||||||
def is_reserved(digits: list) -> bool:
|
address = ip_address(ip)
|
||||||
reserved = [0, 10, 127]
|
if address.is_reserved or address.is_private:
|
||||||
|
return False
|
||||||
if digits[0] in reserved:
|
return True
|
||||||
return True
|
|
||||||
if digits[0] == 192 and digits[1] == 168:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def get_coords(ips: "Path", ip: str) -> Coords:
|
def get_coords(db_path: "Path", ip: str) -> Coords:
|
||||||
split = ip2list(ip)
|
if not is_usable(ip):
|
||||||
|
|
||||||
if is_reserved(split):
|
|
||||||
raise ReservedIPError(f"{ip} is a reserved IP address.")
|
raise ReservedIPError(f"{ip} is a reserved IP address.")
|
||||||
|
try:
|
||||||
prefix = f"^{split[0]}.{split[1]}."
|
lat, lon = read_from_table(db_path, ip)
|
||||||
|
return Coords(lat, lon)
|
||||||
if shutil.which("rg") is not None:
|
except Exception:
|
||||||
args = ["/usr/bin/rg", prefix, str(ips)]
|
raise GeolocationError("No matching records found")
|
||||||
else:
|
|
||||||
args = ["/usr/bin/grep", "-E", prefix, str(ips)]
|
|
||||||
proc = subprocess.run(args, capture_output=True, text=True)
|
|
||||||
|
|
||||||
if proc.returncode != 0:
|
|
||||||
raise GeolocationError("Failed to split records")
|
|
||||||
|
|
||||||
ip_list = proc.stdout.splitlines()
|
|
||||||
|
|
||||||
for address in ip_list:
|
|
||||||
fields = address.split(",")
|
|
||||||
upper = fields[1]
|
|
||||||
|
|
||||||
upper_digits = ip2list(upper)
|
|
||||||
if ip == upper:
|
|
||||||
return Coords(float(fields[-2]), float(fields[-1]))
|
|
||||||
|
|
||||||
if split[2] > upper_digits[2]:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if split[2] == upper_digits[2]:
|
|
||||||
if split[3] > upper_digits[3]:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if split[2] <= upper_digits[2]:
|
|
||||||
return Coords(float(fields[-2]), float(fields[-1]))
|
|
||||||
raise GeolocationError("No matching records found")
|
|
||||||
|
|
||||||
|
|
||||||
def get_local_ip() -> str:
|
def get_local_ip() -> str:
|
||||||
|
|||||||
@@ -297,7 +297,7 @@ class Options:
|
|||||||
init = Init(
|
init = Init(
|
||||||
is_steam_running="Is Steam running? DZGUI must be run on top of Steam.",
|
is_steam_running="Is Steam running? DZGUI must be run on top of Steam.",
|
||||||
is_dayz_running="Is DayZ already running? DZGUI cannot launch DayZ if another process is using it.",
|
is_dayz_running="Is DayZ already running? DZGUI cannot launch DayZ if another process is using it.",
|
||||||
requires_steam="DZGUI requires that Steam or Flatpak Steam be installed on the system.",
|
requires_steam="Your chosen client (Steam/Flatpak Steam) must be installed on the system.",
|
||||||
)
|
)
|
||||||
|
|
||||||
mod_panel = ModPanelStrings(
|
mod_panel = ModPanelStrings(
|
||||||
|
|||||||
@@ -163,18 +163,24 @@ class RefreshButton(IconTextButton):
|
|||||||
self.set_tooltip_text(atomic_buttons.refresh_tooltip)
|
self.set_tooltip_text(atomic_buttons.refresh_tooltip)
|
||||||
|
|
||||||
self.connect("clicked", self._on_refresh_clicked)
|
self.connect("clicked", self._on_refresh_clicked)
|
||||||
emitter.connect("servers_loaded", self.start_decrement)
|
emitter.connect("servers_loaded", self._on_load)
|
||||||
|
emitter.connect("servers_timed_out", self._on_timeout)
|
||||||
|
|
||||||
|
def _on_timeout(self, emitter: "Emitter") -> None:
|
||||||
|
self.is_clicked = True
|
||||||
|
self.start_decrement()
|
||||||
|
|
||||||
|
def _on_load(self, emitter: "Emitter", tab: "ServerTab") -> None:
|
||||||
|
self.start_decrement()
|
||||||
|
|
||||||
def _on_refresh_clicked(self, button: Self) -> None:
|
def _on_refresh_clicked(self, button: Self) -> None:
|
||||||
"""Spawned in a thread"""
|
"""Spawned in a thread"""
|
||||||
if self.controller.get_active_treeview().is_loaded() is False:
|
|
||||||
return
|
|
||||||
self.is_clicked = True
|
self.is_clicked = True
|
||||||
self.controller.refresh_tree()
|
self.controller.refresh_tree()
|
||||||
# TODO: get server tab enum
|
# TODO: get server tab enum
|
||||||
# if LAN tab, reload existing entries in place
|
# if LAN tab, reload existing entries in place
|
||||||
|
|
||||||
def start_decrement(self, emitter: "Emitter", tab: "ServerTab") -> None:
|
def start_decrement(self) -> None:
|
||||||
if self.is_clicked is False:
|
if self.is_clicked is False:
|
||||||
return
|
return
|
||||||
if self.loading:
|
if self.loading:
|
||||||
|
|||||||
@@ -132,9 +132,9 @@ class BootDialog(ColorAwareApp, Gtk.Dialog): # type: ignore
|
|||||||
preboot.signatures,
|
preboot.signatures,
|
||||||
False,
|
False,
|
||||||
),
|
),
|
||||||
(StoredFunc(get_ipdb, self.xdg.ips), preboot.geo, False),
|
(StoredFunc(get_ipdb, self.xdg.db), preboot.geo, False),
|
||||||
# TODO: handle IP DB failure and use coords fallback
|
# TODO: handle IP DB failure and use coords fallback
|
||||||
(StoredFunc(get_local_coords, self.xdg.ips), preboot.coords, True),
|
(StoredFunc(get_local_coords, self.xdg.db), preboot.coords, True),
|
||||||
(StoredFunc(check_updates, self.version), preboot.updates, True),
|
(StoredFunc(check_updates, self.version), preboot.updates, True),
|
||||||
]
|
]
|
||||||
self.results: list[Any] = []
|
self.results: list[Any] = []
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from dzgui.const.constants import (
|
|||||||
HERO_PATH,
|
HERO_PATH,
|
||||||
FLATPAK_ROOT_PATH,
|
FLATPAK_ROOT_PATH,
|
||||||
LEGACY_CONFIG_PATH,
|
LEGACY_CONFIG_PATH,
|
||||||
|
TMP_PATH
|
||||||
)
|
)
|
||||||
from dzgui.const.boilerplate import config_boilerplate
|
from dzgui.const.boilerplate import config_boilerplate
|
||||||
from dzgui.const.endpoints import STEAM_API_SETUP
|
from dzgui.const.endpoints import STEAM_API_SETUP
|
||||||
@@ -432,6 +433,8 @@ class FlatpakOverridePage(EnumeratedWizardPage):
|
|||||||
store = Gtk.ListStore(str)
|
store = Gtk.ListStore(str)
|
||||||
for path in xdg_paths.values():
|
for path in xdg_paths.values():
|
||||||
store.append((str(path),))
|
store.append((str(path),))
|
||||||
|
store.append((TMP_PATH,))
|
||||||
|
|
||||||
tree = SimpleTree(store, wizard.tree_column)
|
tree = SimpleTree(store, wizard.tree_column)
|
||||||
tree.set_halign(Gtk.Align.CENTER)
|
tree.set_halign(Gtk.Align.CENTER)
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -4,7 +4,7 @@ description = "DayZ server browser and mod manager for Linux"
|
|||||||
authors = [
|
authors = [
|
||||||
{name = "aclist"}
|
{name = "aclist"}
|
||||||
]
|
]
|
||||||
version = "7.0.0b28"
|
version = "7.0.0b29"
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
license-files = ["LICENSE"]
|
license-files = ["LICENSE"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@@ -110,10 +110,11 @@ config-settings-package = { pygobject-stubs = { config = "Gtk3,Gdk3,Soup2" } }
|
|||||||
markers = [
|
markers = [
|
||||||
"apitest: checks remote endpoints",
|
"apitest: checks remote endpoints",
|
||||||
"config: config file keys/values",
|
"config: config file keys/values",
|
||||||
"realconfig: depends on actual generated local config file",
|
"flatpak: depends on local flatpak installation",
|
||||||
"mods: tests mod metadata/link creation",
|
"mods: tests mod metadata/link creation",
|
||||||
"pefile: validate PE files",
|
"pefile: validate PE files",
|
||||||
"post_install: requires a completed installation",
|
"post_install: requires a completed installation",
|
||||||
|
"realconfig: depends on actual generated local config file",
|
||||||
"redact: log redaction mechanisms",
|
"redact: log redaction mechanisms",
|
||||||
"slow: long-running tests",
|
"slow: long-running tests",
|
||||||
"webtest: depends on remote endpoint",
|
"webtest: depends on remote endpoint",
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import configparser
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from dzgui.const.constants import FLATPAK_APPID, GTK_APP_ID
|
||||||
|
from dzgui.config.xdg import get_xdg_paths
|
||||||
|
from dzgui.util.flatpak import apply_overrides
|
||||||
|
|
||||||
|
PATHS_KEY = "Context"
|
||||||
|
PATHS_SUBKEY = "filesystems"
|
||||||
|
BUS_KEY = "Session Bus Policy"
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.flatpak
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def xdg_paths() -> dict[str, str]:
|
||||||
|
return get_xdg_paths()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def override(xdg_paths) -> None:
|
||||||
|
apply_overrides(xdg_paths)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def config(xdg_paths) -> configparser.ConfigParser:
|
||||||
|
conf_path = xdg_paths["XDG_DATA_HOME"].parent.joinpath(
|
||||||
|
f"flatpak/overrides/{FLATPAK_APPID}"
|
||||||
|
)
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read(conf_path)
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
def test_session_bus(override, config) -> None:
|
||||||
|
bus = config[BUS_KEY][GTK_APP_ID]
|
||||||
|
assert bus == "own"
|
||||||
|
|
||||||
|
|
||||||
|
def test_paths(override, xdg_paths, config) -> None:
|
||||||
|
expect = xdg_paths
|
||||||
|
paths = config[PATHS_KEY][PATHS_SUBKEY].split(";")
|
||||||
|
assert all(str(path) in paths for path in expect.values())
|
||||||
+44
-7
@@ -1,16 +1,40 @@
|
|||||||
from dzgui.config.ipdb import get_ipdb
|
|
||||||
from pathlib import Path
|
|
||||||
import os
|
import os
|
||||||
|
import sqlite3
|
||||||
import pytest
|
import pytest
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from dzgui.const.endpoints import DB_IP
|
||||||
|
from dzgui.config.ipdb import (
|
||||||
|
create_table,
|
||||||
|
find_download_url,
|
||||||
|
serialize,
|
||||||
|
strip_ipv6,
|
||||||
|
unzip,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_db_len(path: Path) -> int:
|
||||||
|
conn = sqlite3.connect(path)
|
||||||
|
c = conn.cursor()
|
||||||
|
c.execute("SELECT COUNT(*) from ips")
|
||||||
|
db_len = c.fetchone()[0]
|
||||||
|
return db_len
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def csv() -> Path:
|
||||||
|
csv = Path(tempfile.NamedTemporaryFile().name)
|
||||||
|
url = find_download_url(DB_IP)
|
||||||
|
tmp = serialize(url)
|
||||||
|
unzip(tmp, csv)
|
||||||
|
strip_ipv6(csv)
|
||||||
|
return csv
|
||||||
|
|
||||||
@pytest.mark.slow
|
@pytest.mark.slow
|
||||||
def test_stripping():
|
def test_stripping(csv):
|
||||||
d = tempfile.TemporaryDirectory()
|
with open(csv, "rb") as f:
|
||||||
path = Path(d.name).joinpath("ips.csv")
|
|
||||||
get_ipdb(path)
|
|
||||||
with open(path, "rb") as f:
|
|
||||||
first = f.readline().decode().rstrip()
|
first = f.readline().decode().rstrip()
|
||||||
try:
|
try:
|
||||||
f.seek(-2, os.SEEK_END)
|
f.seek(-2, os.SEEK_END)
|
||||||
@@ -25,3 +49,16 @@ def test_stripping():
|
|||||||
|
|
||||||
assert first_els[0] == "0.0.0.0"
|
assert first_els[0] == "0.0.0.0"
|
||||||
assert last_els[0] == "224.0.0.0"
|
assert last_els[0] == "224.0.0.0"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.slow
|
||||||
|
def test_row_parity(csv) -> None:
|
||||||
|
rows = csv.read_text().splitlines()
|
||||||
|
|
||||||
|
db = Path(tempfile.NamedTemporaryFile().name)
|
||||||
|
create_table(db, csv)
|
||||||
|
|
||||||
|
csv_len = len(rows)
|
||||||
|
db_len = get_db_len(db)
|
||||||
|
|
||||||
|
assert csv_len == db_len
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
from dzgui.api.servers import Details
|
from dzgui.api.servers import Details
|
||||||
from dzgui.views.dialogs.servers import ServerDetailsDialog
|
from dzgui.views.dialogs.servers import ServerDetailsDialog
|
||||||
|
|
||||||
@@ -21,6 +23,7 @@ class MockController:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.filterwarnings("ignore::DeprecationWarning")
|
||||||
def test_html_escaping() -> None:
|
def test_html_escaping() -> None:
|
||||||
name = "Test server"
|
name = "Test server"
|
||||||
desc = "<Server description> Game & mods"
|
desc = "<Server description> Game & mods"
|
||||||
@@ -31,5 +34,7 @@ def test_html_escaping() -> None:
|
|||||||
controller = MockController()
|
controller = MockController()
|
||||||
dialog = ServerDetailsDialog(controller, details)
|
dialog = ServerDetailsDialog(controller, details)
|
||||||
dialog.destroy()
|
dialog.destroy()
|
||||||
|
while Gtk.events_pending():
|
||||||
|
Gtk.main_iteration()
|
||||||
|
|
||||||
assert dialog.description.get_text() == desc
|
assert dialog.description.get_text() == desc
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import pytest
|
||||||
import unittest
|
import unittest
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
@@ -9,6 +10,8 @@ from dzgui.const.constants import (
|
|||||||
)
|
)
|
||||||
from dzgui.init import proc
|
from dzgui.init import proc
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.flatpak
|
||||||
|
|
||||||
|
|
||||||
class TestSteamDetection(unittest.TestCase):
|
class TestSteamDetection(unittest.TestCase):
|
||||||
def test_flatpak_detection_with_multiple_running_apps(self):
|
def test_flatpak_detection_with_multiple_running_apps(self):
|
||||||
|
|||||||
Reference in new issue
Block a user