Skip to content

Commit

Permalink
Add Windows exit codes
Browse files Browse the repository at this point in the history
  • Loading branch information
kozlovsky committed Feb 6, 2024
1 parent 13a01cf commit b3fc858
Show file tree
Hide file tree
Showing 7 changed files with 1,776 additions and 5 deletions.
26 changes: 26 additions & 0 deletions src/tribler/core/utilities/exit_codes/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import os

from tribler.core.utilities.exit_codes.tribler_exit_codes import exit_codes

# pylint: disable=import-outside-toplevel


check_win_errors = os.name == 'nt'


def get_error_name(error_code: int) -> str:
if error_code in exit_codes:
return exit_codes[error_code]

if check_win_errors:
# Local import to avoid loading Windows error codes on non-Windows platforms.
from tribler.core.utilities.exit_codes.win_error_codes import win_errors

if error_code in win_errors:
return win_errors[error_code].name

try:
return os.strerror(error_code)
except ValueError:
# On platforms where strerror() returns NULL when given an unknown error number, ValueError is raised.
return 'Unknown error'
11 changes: 11 additions & 0 deletions src/tribler/core/utilities/exit_codes/tribler_exit_codes.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@

EXITCODE_OK = 0 # Normal exit code.
EXITCODE_APPLICATION_ERROR = 1 # Generic error code for application errors.

# Valid range for custom errors is 1..127
EXITCODE_DATABASE_IS_CORRUPTED = 99 # If the Core process finishes with this error, the GUI process restarts it.
EXITCODE_ANOTHER_GUI_PROCESS_IS_RUNNING = 98 # A normal situation when a user double-clicks on the torrent file.
EXITCODE_ANOTHER_CORE_PROCESS_IS_RUNNING = 97 # Should not happen if process locking is working correctly.


exit_codes = {}


for name, value in list(globals().items()):
if name.startswith('EXITCODE_'):
exit_codes[value] = name
28 changes: 28 additions & 0 deletions src/tribler/core/utilities/exit_codes/win_error_codes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from dataclasses import dataclass
from pathlib import Path
from typing import Dict


win_error_codes_filename = Path(__file__).parent / 'win_error_codes.txt'


@dataclass
class ExitCode:
code: int
name: str
description: str


def parse_win_error_codes() -> Dict[int, ExitCode]:
error_codes = {}

with open(win_error_codes_filename, 'r', encoding='utf-8') as file:
for line in file.readlines():
code, name, description = line.split(' ', 2)
code = int(code)
error_codes[code] = ExitCode(code, name, description.strip())

return error_codes


win_errors = parse_win_error_codes()
1,669 changes: 1,669 additions & 0 deletions src/tribler/core/utilities/exit_codes/win_error_codes.txt

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions src/tribler/core/utilities/tests/test_exit_codes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from unittest.mock import patch

from tribler.core.utilities.exit_codes import get_error_name


@patch('tribler.core.utilities.exit_codes.check_win_errors', True)
def test_exit_codes():
assert get_error_name(0) == 'EXITCODE_OK'
assert get_error_name(1) == 'EXITCODE_APPLICATION_ERROR'
assert get_error_name(99) == 'EXITCODE_DATABASE_IS_CORRUPTED'
assert get_error_name(98) == 'EXITCODE_ANOTHER_GUI_PROCESS_IS_RUNNING'
assert get_error_name(97) == 'EXITCODE_ANOTHER_CORE_PROCESS_IS_RUNNING'

assert get_error_name(-1073741819) == 'STATUS_ACCESS_VIOLATION'
assert get_error_name(-1073740940) == 'STATUS_HEAP_CORRUPTION'

with patch('os.strerror', side_effect=ValueError):
assert get_error_name(1000000) == 'Unknown error'
3 changes: 2 additions & 1 deletion src/tribler/gui/core_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from PyQt5.QtCore import QObject, QProcess, QProcessEnvironment, QTimer
from PyQt5.QtNetwork import QNetworkRequest

from tribler.core.utilities.exit_codes import get_error_name
from tribler.core.utilities.exit_codes.tribler_exit_codes import EXITCODE_DATABASE_IS_CORRUPTED
from tribler.core.utilities.process_manager import ProcessManager
from tribler.gui import gui_sentry_reporter
Expand Down Expand Up @@ -289,7 +290,7 @@ def format_error_message(exit_code: int, exit_status: int) -> str:
string_error = "Application error"
else:
try:
string_error = os.strerror(exit_code)
string_error = get_error_name(exit_code)
except ValueError:
# On platforms where strerror() returns NULL when given an unknown error number, ValueError is raised.
string_error = 'unknown error number'
Expand Down
26 changes: 22 additions & 4 deletions src/tribler/gui/tests/test_core_manager.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import errno
import sys
import time
from unittest.mock import MagicMock, patch
Expand Down Expand Up @@ -141,14 +140,33 @@ def test_decode_raw_core_output(core_manager):


def test_format_error_message():
actual = CoreManager.format_error_message(exit_code=errno.ENOENT, exit_status=1)
expected = '''The Tribler core has unexpectedly finished with exit code 2 (0x2) and status: 1.
actual = CoreManager.format_error_message(exit_code=99, exit_status=1)
expected = '''The Tribler core has unexpectedly finished with exit code 99 (0x63) and status: 1.
Error message: No such file or directory'''
Error message: EXITCODE_DATABASE_IS_CORRUPTED'''

assert actual == expected


@patch('tribler.core.utilities.exit_codes.check_win_errors', True)
def test_format_error_message_windows():
actual = CoreManager.format_error_message(exit_code=-1073741819, exit_status=1)
expected = '''The Tribler core has unexpectedly finished with exit code -1073741819 (0xc0000005) and status: 1.
Error message: STATUS_ACCESS_VIOLATION'''
assert actual == expected


@patch('tribler.core.utilities.exit_codes.check_win_errors', False)
@patch('os.strerror', MagicMock(side_effect=ValueError))
def test_format_error_message_windows_error_not_on_windows():
actual = CoreManager.format_error_message(exit_code=-1073741819, exit_status=1)
expected = '''The Tribler core has unexpectedly finished with exit code -1073741819 (0xc0000005) and status: 1.
Error message: Unknown error'''
assert actual == expected


def test_error_code_to_hex_negative():
actual = CoreManager.error_code_to_hex(-1073741819)
expected = '0xc0000005'
Expand Down

0 comments on commit b3fc858

Please sign in to comment.