lint: ruff cleanup pass across bal/ and tests/
- Sort imports and fix pyproject ruff config (per-file ignores for intentional Qt/core exceptions) - Mark Heirs.validate_* helpers as @staticmethod - Clean up dead code, rename shadowing vars, use raise ... from - Add AGENTS.md with env/lint/test/release guidance
This commit is contained in:
@@ -33,7 +33,8 @@ def _active_source_without_strings(module) -> str:
|
||||
if isinstance(node.value, str) and hasattr(node, "end_lineno"):
|
||||
self.spans.append((node.lineno, node.end_lineno))
|
||||
self.generic_visit(node)
|
||||
s = _S(); s.visit(tree)
|
||||
s = _S()
|
||||
s.visit(tree)
|
||||
drop = set()
|
||||
for a, b in s.spans:
|
||||
drop.update(range(a, b + 1))
|
||||
@@ -45,12 +46,13 @@ def _active_source_without_strings(module) -> str:
|
||||
|
||||
def main(pkg: str) -> int:
|
||||
from PyQt6.QtWidgets import QApplication, QDialog, QWidget
|
||||
app = QApplication.instance() or QApplication(sys.argv)
|
||||
_app = QApplication.instance() or QApplication(sys.argv)
|
||||
|
||||
wu = importlib.import_module(pkg + ".gui.qt.window_utils")
|
||||
|
||||
# top_level_of: returns the top-level container of a child widget
|
||||
w = QWidget(); child = QWidget(w)
|
||||
w = QWidget()
|
||||
child = QWidget(w)
|
||||
assert wu.top_level_of(child) is w
|
||||
assert wu.top_level_of(None) is None
|
||||
print("[OK] top_level_of")
|
||||
|
||||
@@ -31,7 +31,7 @@ N = 8 # number of servers
|
||||
|
||||
def main():
|
||||
we_mod = importlib.import_module(f"{PKG}.core.willexecutors")
|
||||
W = we_mod.Willexecutors
|
||||
we_cls = we_mod.Willexecutors
|
||||
|
||||
# ---- 1) ping_servers_parallel: time ~= slowest, not sum ----
|
||||
def slow_get_info(url, we, **kwargs):
|
||||
@@ -43,8 +43,8 @@ def main():
|
||||
we["status"] = 200
|
||||
return we
|
||||
|
||||
orig_get_info = W.get_info_task
|
||||
W.get_info_task = staticmethod(slow_get_info)
|
||||
orig_get_info = we_cls.get_info_task
|
||||
we_cls.get_info_task = staticmethod(slow_get_info)
|
||||
try:
|
||||
wes = {}
|
||||
for i in range(N):
|
||||
@@ -57,7 +57,7 @@ def main():
|
||||
seen.append((url, ok))
|
||||
|
||||
start = time.time()
|
||||
W.ping_servers_parallel(wes, on_each=on_each, max_workers=N)
|
||||
we_cls.ping_servers_parallel(wes, on_each=on_each, max_workers=N)
|
||||
elapsed = time.time() - start
|
||||
|
||||
# Sequential would take ~ N * SLOW. Parallel must be far less.
|
||||
@@ -81,15 +81,15 @@ def main():
|
||||
assert we["status"] == "KO", (url, we)
|
||||
print("[OK] ping results written back into the willexecutors mapping")
|
||||
finally:
|
||||
W.get_info_task = orig_get_info
|
||||
we_cls.get_info_task = orig_get_info
|
||||
|
||||
# ---- 2) push_transactions_parallel: time ~= slowest, not sum ----
|
||||
def slow_push(we, **kwargs):
|
||||
time.sleep(SLOW)
|
||||
return "fail" not in we["url"]
|
||||
|
||||
orig_push = W.push_transactions_to_willexecutor
|
||||
W.push_transactions_to_willexecutor = staticmethod(slow_push)
|
||||
orig_push = we_cls.push_transactions_to_willexecutor
|
||||
we_cls.push_transactions_to_willexecutor = staticmethod(slow_push)
|
||||
try:
|
||||
wes = {}
|
||||
for i in range(N):
|
||||
@@ -106,7 +106,7 @@ def main():
|
||||
pushed.append((url, ok))
|
||||
|
||||
start = time.time()
|
||||
results = W.push_transactions_parallel(wes, on_each=on_each_push,
|
||||
results = we_cls.push_transactions_parallel(wes, on_each=on_each_push,
|
||||
max_workers=N)
|
||||
elapsed = time.time() - start
|
||||
|
||||
@@ -117,11 +117,11 @@ def main():
|
||||
f"(sequential would be ~{sequential:.2f}s)")
|
||||
|
||||
assert len(results) == N, results
|
||||
for url, (ok, exc) in results.items():
|
||||
for url, (ok, _exc) in results.items():
|
||||
assert ok == ("good" in url), (url, ok)
|
||||
print("[OK] push results correct for every server")
|
||||
finally:
|
||||
W.push_transactions_to_willexecutor = orig_push
|
||||
we_cls.push_transactions_to_willexecutor = orig_push
|
||||
|
||||
# ---- 2b) global deadline: a hung server must not block past `deadline` ----
|
||||
def hanging_push(we, **kwargs):
|
||||
@@ -129,8 +129,8 @@ def main():
|
||||
time.sleep(10)
|
||||
return True
|
||||
|
||||
orig_push2 = W.push_transactions_to_willexecutor
|
||||
W.push_transactions_to_willexecutor = staticmethod(hanging_push)
|
||||
orig_push2 = we_cls.push_transactions_to_willexecutor
|
||||
we_cls.push_transactions_to_willexecutor = staticmethod(hanging_push)
|
||||
try:
|
||||
wes = {
|
||||
"https://fast.example": {
|
||||
@@ -146,7 +146,7 @@ def main():
|
||||
return True
|
||||
time.sleep(10)
|
||||
return True
|
||||
W.push_transactions_to_willexecutor = staticmethod(fast_or_hang)
|
||||
we_cls.push_transactions_to_willexecutor = staticmethod(fast_or_hang)
|
||||
|
||||
timed_out = []
|
||||
|
||||
@@ -154,7 +154,7 @@ def main():
|
||||
timed_out.append(url)
|
||||
|
||||
start = time.time()
|
||||
W.push_transactions_parallel(
|
||||
we_cls.push_transactions_parallel(
|
||||
wes, max_workers=2, deadline=1.0, on_timeout=on_timeout
|
||||
)
|
||||
elapsed = time.time() - start
|
||||
@@ -163,7 +163,7 @@ def main():
|
||||
print(f"[OK] global deadline enforced: returned in {elapsed:.1f}s, "
|
||||
f"hung server reported via on_timeout")
|
||||
finally:
|
||||
W.push_transactions_to_willexecutor = orig_push2
|
||||
we_cls.push_transactions_to_willexecutor = orig_push2
|
||||
|
||||
# ---- 2c) on_tick is fired periodically from the CALLING thread ----
|
||||
# The elapsed-time counter is driven by an on_tick callback called from the
|
||||
@@ -175,8 +175,8 @@ def main():
|
||||
time.sleep(SLOW * 6) # ~3s, long enough for several ticks
|
||||
return True
|
||||
|
||||
orig_push3 = W.push_transactions_to_willexecutor
|
||||
W.push_transactions_to_willexecutor = staticmethod(slow_push2)
|
||||
orig_push3 = we_cls.push_transactions_to_willexecutor
|
||||
we_cls.push_transactions_to_willexecutor = staticmethod(slow_push2)
|
||||
try:
|
||||
wes = {
|
||||
"https://tick.example": {
|
||||
@@ -191,7 +191,7 @@ def main():
|
||||
ticks.append(time.time())
|
||||
tick_threads.add(threading.current_thread())
|
||||
|
||||
W.push_transactions_parallel(
|
||||
we_cls.push_transactions_parallel(
|
||||
wes, max_workers=1, on_tick=on_tick, tick_interval=0.5
|
||||
)
|
||||
# ~3s push with 0.5s ticks => at least a few ticks.
|
||||
@@ -202,7 +202,7 @@ def main():
|
||||
)
|
||||
print(f"[OK] on_tick fired {len(ticks)} times from the calling thread")
|
||||
finally:
|
||||
W.push_transactions_to_willexecutor = orig_push3
|
||||
we_cls.push_transactions_to_willexecutor = orig_push3
|
||||
|
||||
# ---- 2d) check_transactions_parallel: parallel + deadline + on_tick ----
|
||||
# Pressing "Check" verifies each will-executor still holds its tx. This used
|
||||
@@ -214,8 +214,8 @@ def main():
|
||||
time.sleep(SLOW)
|
||||
return {"tx": "ok"} if "good" in url else None
|
||||
|
||||
orig_check = W.check_transaction
|
||||
W.check_transaction = staticmethod(slow_check)
|
||||
orig_check = we_cls.check_transaction
|
||||
we_cls.check_transaction = staticmethod(slow_check)
|
||||
try:
|
||||
targets = []
|
||||
for i in range(N):
|
||||
@@ -228,7 +228,7 @@ def main():
|
||||
checked.append((wid, res))
|
||||
|
||||
start = time.time()
|
||||
results = W.check_transactions_parallel(
|
||||
results = we_cls.check_transactions_parallel(
|
||||
targets, on_each=on_each_check, max_workers=N
|
||||
)
|
||||
elapsed = time.time() - start
|
||||
@@ -239,7 +239,7 @@ def main():
|
||||
print(f"[OK] check parallel: {elapsed:.2f}s for {N} servers "
|
||||
f"(sequential would be ~{sequential:.2f}s)")
|
||||
finally:
|
||||
W.check_transaction = orig_check
|
||||
we_cls.check_transaction = orig_check
|
||||
|
||||
# 2d-bis) global deadline + on_tick from the calling thread
|
||||
def hanging_check(txid, url, **kwargs):
|
||||
@@ -248,8 +248,8 @@ def main():
|
||||
time.sleep(10)
|
||||
return {"tx": "ok"}
|
||||
|
||||
orig_check2 = W.check_transaction
|
||||
W.check_transaction = staticmethod(hanging_check)
|
||||
orig_check2 = we_cls.check_transaction
|
||||
we_cls.check_transaction = staticmethod(hanging_check)
|
||||
try:
|
||||
targets = [
|
||||
("idf", "https://fast.example"),
|
||||
@@ -268,7 +268,7 @@ def main():
|
||||
tick_threads.add(threading.current_thread())
|
||||
|
||||
start = time.time()
|
||||
W.check_transactions_parallel(
|
||||
we_cls.check_transactions_parallel(
|
||||
targets, max_workers=2, deadline=2.0,
|
||||
on_timeout=on_timeout_check, on_tick=on_tick_check,
|
||||
tick_interval=0.5,
|
||||
@@ -282,7 +282,7 @@ def main():
|
||||
print(f"[OK] check global deadline enforced ({elapsed:.1f}s), on_tick "
|
||||
f"fired {len(ticks)}x from the calling thread")
|
||||
finally:
|
||||
W.check_transaction = orig_check2
|
||||
we_cls.check_transaction = orig_check2
|
||||
|
||||
# ---- 3) the wizard's loop_push must use the parallel helper ----
|
||||
# The "Building Will" wizard broadcasts via BalBuildWillDialog.loop_push.
|
||||
|
||||
@@ -17,8 +17,8 @@ import sys
|
||||
|
||||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||
|
||||
from PyQt6.QtWidgets import QApplication, QLabel, QWidget, QVBoxLayout
|
||||
from PyQt6.QtCore import Qt
|
||||
from PyQt6.QtWidgets import QApplication, QLabel, QVBoxLayout, QWidget
|
||||
|
||||
# Same colors as BalBuildWillDialog
|
||||
COLOR_WARNING = "#cfa808"
|
||||
|
||||
@@ -17,10 +17,15 @@ import os
|
||||
|
||||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||
|
||||
from PyQt6.QtWidgets import ( # noqa: E402
|
||||
QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton,
|
||||
)
|
||||
from PyQt6.QtCore import Qt # noqa: E402
|
||||
from PyQt6.QtWidgets import ( # noqa: E402
|
||||
QApplication,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QPushButton,
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
)
|
||||
|
||||
COLOR_OK = "#05ad05"
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@ import sys
|
||||
|
||||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||
|
||||
from PyQt6.QtWidgets import QApplication, QLabel, QWidget, QVBoxLayout
|
||||
from PyQt6.QtCore import Qt
|
||||
from PyQt6.QtWidgets import QApplication, QLabel, QVBoxLayout, QWidget
|
||||
|
||||
COLOR_ERROR = "#ff0000"
|
||||
COLOR_OK = "#05ad05"
|
||||
|
||||
@@ -20,12 +20,17 @@ import os
|
||||
|
||||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||
|
||||
from PyQt6.QtWidgets import ( # noqa: E402
|
||||
QApplication, QWidget, QHBoxLayout, QPushButton, QComboBox, QLineEdit,
|
||||
QLabel,
|
||||
)
|
||||
from PyQt6.QtCore import QSize # noqa: E402
|
||||
from PyQt6.QtGui import QIcon, QPixmap # noqa: E402
|
||||
from PyQt6.QtCore import QSize, Qt # noqa: E402
|
||||
from PyQt6.QtWidgets import ( # noqa: E402
|
||||
QApplication,
|
||||
QComboBox,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QLineEdit,
|
||||
QPushButton,
|
||||
QWidget,
|
||||
)
|
||||
|
||||
ICON_PATH = os.path.join(os.path.dirname(__file__), "..", "bal", "icons",
|
||||
"wizard.png")
|
||||
|
||||
@@ -27,12 +27,19 @@ import os
|
||||
|
||||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||
|
||||
from PyQt6.QtWidgets import ( # noqa: E402
|
||||
QApplication, QWidget, QVBoxLayout, QHBoxLayout, QToolButton, QComboBox,
|
||||
QLineEdit, QSpinBox, QLabel,
|
||||
)
|
||||
from PyQt6.QtGui import QFontMetrics # noqa: E402
|
||||
from PyQt6.QtCore import Qt # noqa: E402
|
||||
from PyQt6.QtGui import QFontMetrics # noqa: E402
|
||||
from PyQt6.QtWidgets import ( # noqa: E402
|
||||
QApplication,
|
||||
QComboBox,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QLineEdit,
|
||||
QSpinBox,
|
||||
QToolButton,
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
)
|
||||
|
||||
|
||||
def _char_w():
|
||||
|
||||
@@ -21,18 +21,21 @@ Run:
|
||||
QT_QPA_PLATFORM=offscreen PYTHONPATH=electrum-src python3 tests/sim_update_flows.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import copy
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
|
||||
|
||||
from bal.core.will import (
|
||||
WillItem, Will,
|
||||
NotCompleteWillException, HeirNotFoundException, NoHeirsException,
|
||||
TxFeesChangedException, WillExpiredException,
|
||||
HeirNotFoundException,
|
||||
NoHeirsException,
|
||||
NotCompleteWillException,
|
||||
TxFeesChangedException,
|
||||
Will,
|
||||
WillExpiredException,
|
||||
WillItem,
|
||||
)
|
||||
from bal.core.util import Util
|
||||
|
||||
# A valid serialized tx (1 input + 1 output, version 2). Its nLockTime is 0.
|
||||
_VALID_TX_HEX = (
|
||||
|
||||
@@ -26,30 +26,28 @@ def main():
|
||||
from PyQt6.QtWidgets import QApplication # noqa
|
||||
_app = QApplication.instance() or QApplication([])
|
||||
|
||||
results = {}
|
||||
|
||||
# 1) Core modules import (these must be GUI-free).
|
||||
bal = imp_core("bal", "core.plugin_base")
|
||||
util = imp_core("util", "core.util")
|
||||
heirs = imp_core("heirs", "core.heirs")
|
||||
will = imp_core("will", "core.will")
|
||||
we = imp_core("willexecutors", "core.willexecutors")
|
||||
_we = imp_core("willexecutors", "core.willexecutors")
|
||||
|
||||
# 2) GUI module imports.
|
||||
qt = imp_gui()
|
||||
|
||||
# 3) Behaviour checks (pure logic, must be identical across versions).
|
||||
BalTimestamp = bal.BalTimestamp
|
||||
assert BalTimestamp("30d").duration_to_days() == 30, "BalTimestamp 30d"
|
||||
assert BalTimestamp("1y").duration_to_days() == 365, "BalTimestamp 1y"
|
||||
assert str(BalTimestamp("7d")) == "7d", "BalTimestamp str"
|
||||
bal_timestamp = bal.BalTimestamp
|
||||
assert bal_timestamp("30d").duration_to_days() == 30, "BalTimestamp 30d"
|
||||
assert bal_timestamp("1y").duration_to_days() == 365, "BalTimestamp 1y"
|
||||
assert str(bal_timestamp("7d")) == "7d", "BalTimestamp str"
|
||||
|
||||
Util = util.Util
|
||||
assert Util.is_perc("50%") is True
|
||||
assert Util.is_perc("100") is False
|
||||
assert Util.text_to_hex("BAL") == "42414c"
|
||||
assert Util.hex_to_text("42414c") == "BAL"
|
||||
assert Util.int_locktime(days=1) == 86400
|
||||
util_cls = util.Util
|
||||
assert util_cls.is_perc("50%") is True
|
||||
assert util_cls.is_perc("100") is False
|
||||
assert util_cls.text_to_hex("BAL") == "42414c"
|
||||
assert util_cls.hex_to_text("42414c") == "BAL"
|
||||
assert util_cls.int_locktime(days=1) == 86400
|
||||
|
||||
# heirs constants must keep the same column layout (very delicate!)
|
||||
assert heirs.HEIR_ADDRESS == 0
|
||||
|
||||
@@ -27,19 +27,19 @@ Run:
|
||||
tests/test_anticipate_manual_locktime.py -q
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import copy
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
|
||||
|
||||
import pytest # noqa: E402
|
||||
|
||||
from bal.core.will import ( # noqa: E402
|
||||
WillItem,
|
||||
Will,
|
||||
NotCompleteWillException,
|
||||
Will,
|
||||
WillExpiredException,
|
||||
WillItem,
|
||||
)
|
||||
|
||||
# A valid serialized tx (1 input + 1 output, version 2).
|
||||
|
||||
@@ -22,13 +22,13 @@ whether a fix is needed. Run:
|
||||
python3 -m pytest tests/test_anticipate_past_locktime.py -q
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
|
||||
|
||||
from bal.core.util import Util, LOCKTIME_THRESHOLD
|
||||
from bal.core.util import LOCKTIME_THRESHOLD, Util
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -9,25 +9,34 @@ Run:
|
||||
python3 tests/test_core_heirs.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
|
||||
|
||||
from bal.core.heirs import (
|
||||
HEIR_ADDRESS, HEIR_AMOUNT, HEIR_LOCKTIME, HEIR_REAL_AMOUNT,
|
||||
HEIR_DUST_AMOUNT, TRANSACTION_LABEL,
|
||||
HEIR_ADDRESS,
|
||||
HEIR_AMOUNT,
|
||||
HEIR_DUST_AMOUNT,
|
||||
HEIR_LOCKTIME,
|
||||
HEIR_REAL_AMOUNT,
|
||||
OP_RETURN_PREFIX,
|
||||
create_op_return_script,
|
||||
is_op_return_address, get_op_return_hex, validate_op_return_hex,
|
||||
TRANSACTION_LABEL,
|
||||
AliasNotFoundException,
|
||||
NotAnAddress, AmountNotValid, LocktimeNotValid,
|
||||
HeirExpiredException, HeirAmountIsDustException,
|
||||
NoHeirsException, WillExecutorFeeException,
|
||||
AmountNotValid,
|
||||
BalanceTooLowException,
|
||||
HeirAmountIsDustException,
|
||||
Heirs,
|
||||
LocktimeNotValid,
|
||||
NoHeirsException,
|
||||
NotAnAddress,
|
||||
WillExecutorFeeException,
|
||||
create_op_return_script,
|
||||
get_op_return_hex,
|
||||
is_op_return_address,
|
||||
validate_op_return_hex,
|
||||
)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# Constants
|
||||
# ------------------------------------------------------------------ #
|
||||
@@ -70,7 +79,7 @@ def test_op_return_empty():
|
||||
def test_op_return_too_big():
|
||||
try:
|
||||
create_op_return_script("ab" * 81) # 81 bytes > max 80
|
||||
assert False, "expected ValueError"
|
||||
raise AssertionError("expected ValueError")
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
@@ -179,13 +188,13 @@ def test_validate_amount():
|
||||
# Invalid
|
||||
try:
|
||||
Heirs.validate_amount("0.000000001")
|
||||
assert False, "expected AmountNotValid"
|
||||
raise AssertionError("expected AmountNotValid")
|
||||
except AmountNotValid:
|
||||
pass
|
||||
|
||||
try:
|
||||
Heirs.validate_amount("-1")
|
||||
assert False, "expected AmountNotValid"
|
||||
raise AssertionError("expected AmountNotValid")
|
||||
except AmountNotValid:
|
||||
pass
|
||||
|
||||
@@ -209,7 +218,7 @@ def test_validate_locktime_expired():
|
||||
past = int(time.time()) - 86400 # yesterday
|
||||
try:
|
||||
Heirs.validate_locktime(str(past), timestamp_to_check=past + 1)
|
||||
assert False, "expected LocktimeNotValid"
|
||||
raise AssertionError("expected LocktimeNotValid")
|
||||
except LocktimeNotValid:
|
||||
pass
|
||||
|
||||
@@ -289,7 +298,7 @@ def test_validate_op_return_hex_valid():
|
||||
def test_validate_op_return_hex_invalid():
|
||||
try:
|
||||
validate_op_return_hex("nothex!!")
|
||||
assert False, "expected NotAnAddress"
|
||||
raise AssertionError("expected NotAnAddress")
|
||||
except NotAnAddress:
|
||||
pass
|
||||
|
||||
@@ -297,7 +306,7 @@ def test_validate_op_return_hex_invalid():
|
||||
def test_validate_op_return_hex_too_long():
|
||||
try:
|
||||
validate_op_return_hex("ab" * 81)
|
||||
assert False, "expected NotAnAddress"
|
||||
raise AssertionError("expected NotAnAddress")
|
||||
except NotAnAddress:
|
||||
pass
|
||||
|
||||
@@ -355,4 +364,4 @@ if __name__ == "__main__":
|
||||
if name.startswith("test_"):
|
||||
globals()[name]()
|
||||
print(f" [OK] {name}")
|
||||
print(f"[OK] All heirs tests passed")
|
||||
print("[OK] All heirs tests passed")
|
||||
|
||||
@@ -8,19 +8,20 @@ Run:
|
||||
QT_QPA_PLATFORM=offscreen python3 tests/test_core_heirs_extra.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import sys
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
|
||||
|
||||
from bal.core.heirs import (
|
||||
Heirs, create_op_return_script, reduce_outputs,
|
||||
HEIR_ADDRESS, HEIR_AMOUNT, HEIR_LOCKTIME, HEIR_REAL_AMOUNT,
|
||||
HEIR_AMOUNT,
|
||||
Heirs,
|
||||
create_op_return_script,
|
||||
reduce_outputs,
|
||||
)
|
||||
from bal.core.willexecutors import Willexecutors
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# Heirs db-dependent methods
|
||||
# ------------------------------------------------------------------ #
|
||||
@@ -188,7 +189,7 @@ def test_validate_address_invalid():
|
||||
from bal.core.heirs import NotAnAddress
|
||||
try:
|
||||
Heirs.validate_address("bad")
|
||||
assert False, "should have raised"
|
||||
raise AssertionError("should have raised")
|
||||
except NotAnAddress:
|
||||
pass
|
||||
|
||||
|
||||
@@ -8,14 +8,15 @@ Run:
|
||||
python3 tests/test_core_plugin_base.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
|
||||
|
||||
from datetime import datetime, date, timedelta
|
||||
from bal.core.plugin_base import BalTimestamp, BalPlugin, BalConfig
|
||||
from datetime import date, datetime, timedelta
|
||||
|
||||
from bal.core.plugin_base import BalConfig, BalPlugin, BalTimestamp
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# BalTimestamp
|
||||
|
||||
@@ -9,13 +9,14 @@ Run:
|
||||
python3 tests/test_core_util.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
|
||||
|
||||
import pytest
|
||||
|
||||
from bal.core.util import Util, LOCKTIME_THRESHOLD
|
||||
from bal.core.util import Util
|
||||
|
||||
|
||||
def test_locktime_to_str():
|
||||
@@ -40,7 +41,7 @@ def test_str_to_locktime():
|
||||
|
||||
# the block-height suffix "b" was removed (A1): "144b" is no longer a valid
|
||||
# relative locktime, so it is NOT passed through unchanged.
|
||||
with pytest.raises(Exception):
|
||||
with pytest.raises(ValueError):
|
||||
Util.str_to_locktime("144b")
|
||||
|
||||
# integer string -> int
|
||||
@@ -347,44 +348,44 @@ def test_in_utxo():
|
||||
|
||||
|
||||
def test_cmp_output():
|
||||
class O:
|
||||
class Obj:
|
||||
def __init__(self, addr, val):
|
||||
self.address = addr
|
||||
self.value = val
|
||||
assert Util.cmp_output(O("a", 100), O("a", 100)) is True
|
||||
assert Util.cmp_output(O("a", 100), O("b", 100)) is False
|
||||
assert Util.cmp_output(O("a", 100), O("a", 200)) is False
|
||||
assert Util.cmp_output(Obj("a", 100), Obj("a", 100)) is True
|
||||
assert Util.cmp_output(Obj("a", 100), Obj("b", 100)) is False
|
||||
assert Util.cmp_output(Obj("a", 100), Obj("a", 200)) is False
|
||||
|
||||
|
||||
def test_in_output():
|
||||
class O:
|
||||
class Obj:
|
||||
def __init__(self, addr, val):
|
||||
self.address = addr
|
||||
self.value = val
|
||||
outputs = [O("a", 100), O("b", 200)]
|
||||
assert Util.in_output(O("a", 100), outputs) is True
|
||||
assert Util.in_output(O("z", 999), outputs) is False
|
||||
assert Util.in_output(O("a", 100), []) is False
|
||||
outputs = [Obj("a", 100), Obj("b", 200)]
|
||||
assert Util.in_output(Obj("a", 100), outputs) is True
|
||||
assert Util.in_output(Obj("z", 999), outputs) is False
|
||||
assert Util.in_output(Obj("a", 100), []) is False
|
||||
|
||||
|
||||
def test_din_output():
|
||||
class O:
|
||||
class Obj:
|
||||
def __init__(self, addr, val):
|
||||
self.address = addr
|
||||
self.value = val
|
||||
|
||||
outputs = [O("a", 100), O("b", 200)]
|
||||
outputs = [Obj("a", 100), Obj("b", 200)]
|
||||
|
||||
# same amount AND same address
|
||||
same_amt, same_addr = Util.din_output(O("a", 100), outputs)
|
||||
same_amt, same_addr = Util.din_output(Obj("a", 100), outputs)
|
||||
assert same_amt is True and same_addr is True
|
||||
|
||||
# same amount but different address
|
||||
same_amt, same_addr = Util.din_output(O("c", 100), outputs)
|
||||
same_amt, same_addr = Util.din_output(Obj("c", 100), outputs)
|
||||
assert same_amt is True and same_addr is False
|
||||
|
||||
# different amount
|
||||
same_amt, same_addr = Util.din_output(O("z", 999), outputs)
|
||||
same_amt, same_addr = Util.din_output(Obj("z", 999), outputs)
|
||||
assert same_amt is False and same_addr is False
|
||||
|
||||
|
||||
|
||||
@@ -8,13 +8,13 @@ Run:
|
||||
python3 tests/test_core_will.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import copy
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
|
||||
|
||||
from bal.core.will import WillItem, Will
|
||||
from bal.core.willexecutors import Willexecutors
|
||||
from bal.core.will import Will, WillItem
|
||||
|
||||
# A valid serialized Bitcoin transaction hex (1 input + 1 P2PKH output, version 2)
|
||||
_VALID_TX_HEX = (
|
||||
@@ -332,11 +332,19 @@ def test_will_check_tx_height():
|
||||
|
||||
def test_exceptions():
|
||||
from bal.core.will import (
|
||||
WillException, WillExpiredException, NotCompleteWillException,
|
||||
HeirChangeException, TxFeesChangedException, HeirNotFoundException,
|
||||
WillexecutorChangeException, NoWillExecutorNotPresent,
|
||||
WillExecutorNotPresent, NoHeirsException,
|
||||
AmountException, PercAmountException, FixedAmountException,
|
||||
AmountException,
|
||||
FixedAmountException,
|
||||
HeirChangeException,
|
||||
HeirNotFoundException,
|
||||
NoHeirsException,
|
||||
NotCompleteWillException,
|
||||
NoWillExecutorNotPresent,
|
||||
PercAmountException,
|
||||
TxFeesChangedException,
|
||||
WillException,
|
||||
WillexecutorChangeException,
|
||||
WillExecutorNotPresent,
|
||||
WillExpiredException,
|
||||
WillPostponedException,
|
||||
)
|
||||
|
||||
@@ -375,4 +383,4 @@ if __name__ == "__main__":
|
||||
if name.startswith("test_"):
|
||||
globals()[name]()
|
||||
print(f" [OK] {name}")
|
||||
print(f"[OK] All Will tests passed")
|
||||
print("[OK] All Will tests passed")
|
||||
|
||||
@@ -8,15 +8,16 @@ Run:
|
||||
QT_QPA_PLATFORM=offscreen python3 tests/test_core_will_extra.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
from unittest.mock import MagicMock, patch, PropertyMock, call
|
||||
import sys
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
|
||||
|
||||
from bal.core.will import Will, WillItem
|
||||
from electrum.transaction import Transaction
|
||||
|
||||
from bal.core.will import Will, WillItem
|
||||
|
||||
_VALID_TX_HEX = (
|
||||
"01000000012a5c9a94fcde98f5581cd00162c60a13936ceb75389ea65b"
|
||||
"f38633b424eb4031000000006c493046022100a82bbc57a0136751e543"
|
||||
|
||||
@@ -22,15 +22,14 @@ import os
|
||||
import sys
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
|
||||
|
||||
from bal.core.will import Will, WillItem
|
||||
|
||||
# Patch Transaction.add_info_from_wallet so WillItem can parse the tx hex
|
||||
# without a live Electrum wallet connection.
|
||||
from electrum.transaction import Transaction
|
||||
|
||||
from bal.core.will import Will, WillItem
|
||||
|
||||
_patcher = patch.object(Transaction, "add_info_from_wallet")
|
||||
_patcher.start()
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
|
||||
from bal.core.plugin_base import BalConfig
|
||||
from bal.core.willexecutors import Willexecutors
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# Mocks
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
@@ -27,7 +27,6 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
|
||||
|
||||
from bal.core.plugin_base import BalConfig
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# Mocks
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
@@ -27,7 +27,6 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
|
||||
from bal.core.plugin_base import BalConfig
|
||||
from bal.gui.qt.widgets import compute_reminder_offsets
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# Mocks
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
@@ -25,7 +25,6 @@ import copy
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -42,15 +41,12 @@ from electrum import bitcoin
|
||||
from electrum.transaction import (
|
||||
PartialTransaction,
|
||||
PartialTxInput,
|
||||
PartialTxOutput,
|
||||
TxOutpoint,
|
||||
)
|
||||
from electrum.util import bfh
|
||||
|
||||
from bal.core.heirs import Heirs
|
||||
from bal.core.will import Will, WillItem
|
||||
from bal.core.willexecutors import Willexecutors
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# Load karen7 wallet data
|
||||
@@ -166,7 +162,7 @@ def _build_utxo_value_map(data):
|
||||
for txid, outputs in txo.items():
|
||||
if not isinstance(outputs, dict):
|
||||
continue
|
||||
for addr, out_map in outputs.items():
|
||||
for _, out_map in outputs.items():
|
||||
if not isinstance(out_map, dict):
|
||||
continue
|
||||
for idx, info in out_map.items():
|
||||
@@ -188,7 +184,7 @@ def _populate_input_values(will, utxo_value_map):
|
||||
This is the equivalent of what ``add_info_from_wallet`` does in the
|
||||
real flow: looking up the UTXO value and attaching it to the input.
|
||||
"""
|
||||
for wid, wi in will.items():
|
||||
for _, wi in will.items():
|
||||
for txin in wi.tx.inputs():
|
||||
prevout_str = txin.prevout.to_str()
|
||||
if txin._trusted_value_sats is None and prevout_str in utxo_value_map:
|
||||
|
||||
@@ -36,13 +36,12 @@ import pytest
|
||||
# below the repo root that contains the ``bal`` package).
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
|
||||
|
||||
from bal.core.will import WillItem, Will, HeirNotFoundException
|
||||
from bal.core.heirs import Heirs
|
||||
from bal.core.will import HeirNotFoundException, Will, WillItem
|
||||
from bal.core.willexecutors import Willexecutors
|
||||
from bal.gui.qt.calendar import BalCalendar
|
||||
from bal.gui.qt.widgets import compute_reminder_offsets
|
||||
|
||||
|
||||
# A valid serialized Bitcoin transaction hex (1 input + 1 P2PKH output,
|
||||
# version 2). Reused from test_core_will.py so WillItem can parse a real tx.
|
||||
_VALID_TX_HEX = (
|
||||
@@ -550,7 +549,7 @@ def test_e5_build_with_real_wallet_heirs_and_utxos():
|
||||
for txid, outputs in txo.items():
|
||||
if not isinstance(outputs, dict):
|
||||
continue
|
||||
for addr, out_map in outputs.items():
|
||||
for _, out_map in outputs.items():
|
||||
if not isinstance(out_map, dict):
|
||||
continue
|
||||
for idx, info in out_map.items():
|
||||
|
||||
@@ -16,8 +16,9 @@ Run:
|
||||
python3 tests/test_group_f_heir_change_rebuild.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
|
||||
|
||||
from bal.core.will import Will
|
||||
|
||||
@@ -24,8 +24,7 @@ import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
|
||||
|
||||
from bal.gui.qt.widgets import (BASIC_REMINDER_OFFSETS,
|
||||
basic_reminder_offsets)
|
||||
from bal.gui.qt.widgets import BASIC_REMINDER_OFFSETS, basic_reminder_offsets
|
||||
|
||||
|
||||
def test_basic_offsets_all_future():
|
||||
|
||||
@@ -32,7 +32,6 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
|
||||
|
||||
from bal.gui.qt.window import BalWindow # noqa: E402 (path insert above)
|
||||
|
||||
|
||||
OFFSET = BalWindow.BASIC_MODE_CHECK_ALIVE_OFFSET_SECONDS
|
||||
|
||||
|
||||
|
||||
@@ -10,14 +10,12 @@ Run:
|
||||
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
from datetime import datetime, timezone
|
||||
|
||||
sys.path.insert(0, __file__.rsplit("/", 2)[0])
|
||||
|
||||
from bal.gui.qt.calendar import BalCalendar
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# format_time
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
@@ -8,12 +8,13 @@ Run:
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, __file__.rsplit("/", 2)[0])
|
||||
|
||||
from PyQt6.QtWidgets import QApplication, QGridLayout, QLabel, QWidget
|
||||
from PyQt6.QtWidgets import QApplication, QGridLayout, QLabel
|
||||
|
||||
# Import the module itself, not via "from .common import *"
|
||||
import bal.gui.qt.common as C
|
||||
import bal.gui.qt.common as common
|
||||
|
||||
_app = QApplication.instance() or QApplication(sys.argv)
|
||||
|
||||
@@ -23,18 +24,18 @@ _app = QApplication.instance() or QApplication(sys.argv)
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
def test_shown_cv_default():
|
||||
cv = C.shown_cv(True)
|
||||
cv = common.shown_cv(True)
|
||||
assert cv.get() is True
|
||||
|
||||
|
||||
def test_shown_cv_set():
|
||||
cv = C.shown_cv(True)
|
||||
cv = common.shown_cv(True)
|
||||
cv.set(False)
|
||||
assert cv.get() is False
|
||||
|
||||
|
||||
def test_shown_cv_roundtrip():
|
||||
cv = C.shown_cv(False)
|
||||
cv = common.shown_cv(False)
|
||||
assert cv.get() is False
|
||||
cv.set(True)
|
||||
assert cv.get() is True
|
||||
@@ -47,19 +48,19 @@ def test_shown_cv_roundtrip():
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
def test_check_alive_error_default():
|
||||
err = C.CheckAliveError(1000000)
|
||||
err = common.CheckAliveError(1000000)
|
||||
assert err.timestamp_to_check == 1000000
|
||||
|
||||
|
||||
def test_check_alive_error_str():
|
||||
err = C.CheckAliveError(1000000)
|
||||
err = common.CheckAliveError(1000000)
|
||||
s = str(err)
|
||||
assert "Check alive expired" in s
|
||||
assert "1970" in s
|
||||
|
||||
|
||||
def test_check_alive_error_subclass():
|
||||
assert issubclass(C.CheckAliveError, Exception)
|
||||
assert issubclass(common.CheckAliveError, Exception)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
@@ -68,17 +69,15 @@ def test_check_alive_error_subclass():
|
||||
|
||||
def test_add_widget():
|
||||
grid = QGridLayout()
|
||||
parent = QWidget()
|
||||
label = QLabel("test")
|
||||
C.add_widget(grid, "Label", label, 0, "Help text")
|
||||
common.add_widget(grid, "Label", label, 0, "Help text")
|
||||
assert grid.count() == 3 # label + widget + help button
|
||||
|
||||
|
||||
def test_add_widget_multiple_rows():
|
||||
grid = QGridLayout()
|
||||
parent = QWidget()
|
||||
C.add_widget(grid, "A", QLabel("a"), 0, "help_a")
|
||||
C.add_widget(grid, "B", QLabel("b"), 1, "help_b")
|
||||
common.add_widget(grid, "A", QLabel("a"), 0, "help_a")
|
||||
common.add_widget(grid, "B", QLabel("b"), 1, "help_b")
|
||||
assert grid.count() == 6
|
||||
|
||||
|
||||
@@ -87,7 +86,7 @@ def test_add_widget_multiple_rows():
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
def test_log_error_no_window():
|
||||
C.log_error((Exception, Exception("test"), None))
|
||||
common.log_error((Exception, Exception("test"), None))
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
@@ -8,11 +8,11 @@ Run:
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, __file__.rsplit("/", 2)[0])
|
||||
|
||||
from bal.gui.qt.theme import status_color
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# Helpers
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
@@ -13,13 +13,11 @@ Run:
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, __file__.rsplit("/", 2)[0])
|
||||
|
||||
from PyQt6.QtCore import QTimer
|
||||
from PyQt6.QtWidgets import QApplication, QWidget
|
||||
|
||||
from electrum.util import DECIMAL_POINT, decimal_point_to_base_unit_name
|
||||
|
||||
_app = QApplication.instance() or QApplication(sys.argv)
|
||||
|
||||
|
||||
|
||||
@@ -8,13 +8,18 @@ Run:
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, __file__.rsplit("/", 2)[0])
|
||||
|
||||
from PyQt6.QtCore import QTimer
|
||||
from PyQt6.QtWidgets import QApplication, QDialog, QWidget
|
||||
|
||||
from bal.gui.qt.window_utils import (
|
||||
bring_to_front, show_modal, show_on_top, stop_thread, top_level_of,
|
||||
bring_to_front,
|
||||
show_modal,
|
||||
show_on_top,
|
||||
stop_thread,
|
||||
top_level_of,
|
||||
)
|
||||
|
||||
_app = QApplication.instance() or QApplication(sys.argv)
|
||||
|
||||
@@ -33,10 +33,9 @@ import logging
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from unittest.mock import MagicMock, patch
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from electrum import constants
|
||||
|
||||
constants.net = constants.BitcoinRegtest
|
||||
@@ -48,10 +47,14 @@ from electrum.transaction import PartialTxInput, TxOutpoint
|
||||
from electrum.util import bfh
|
||||
|
||||
from bal.core.heirs import Heirs
|
||||
from bal.core.will import Will, WillItem, NoWillExecutorNotPresent, NotCompleteWillException
|
||||
from bal.core.will import (
|
||||
NotCompleteWillException,
|
||||
NoWillExecutorNotPresent,
|
||||
Will,
|
||||
WillItem,
|
||||
)
|
||||
from bal.core.willexecutors import Willexecutors
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# Load karen7 wallet data
|
||||
# ------------------------------------------------------------------ #
|
||||
@@ -479,11 +482,11 @@ class TestNoWillexecutorKaren7:
|
||||
dialog = FakeBuildWillDialog(self.bal_window)
|
||||
dialog.task_phase1()
|
||||
assert any(
|
||||
"Not present - select one or enable backup mode" in l
|
||||
for l in dialog.labels
|
||||
"Not present - select one or enable backup mode" in label
|
||||
for label in dialog.labels
|
||||
), "dialog labels must contain the 'not present' message"
|
||||
assert any(
|
||||
"#ff0000" in l for l in dialog.labels
|
||||
"#ff0000" in label for label in dialog.labels
|
||||
), "dialog labels must use red (COLOR_ERROR)"
|
||||
|
||||
def test_task_phase1_adds_action_buttons(self):
|
||||
|
||||
@@ -23,9 +23,15 @@ if os.path.isdir(ELECTRUM_DIR):
|
||||
sys.path.insert(0, ELECTRUM_DIR)
|
||||
|
||||
from bal.core.heirs import Heirs
|
||||
from bal.core.willexecutors import Willexecutors
|
||||
from bal.core.will import Will, WillItem, NotCompleteWillException, NoHeirsException, NoWillExecutorNotPresent
|
||||
from bal.core.plugin_base import BalPlugin, BalTimestamp
|
||||
from bal.core.will import (
|
||||
NoHeirsException,
|
||||
NotCompleteWillException,
|
||||
NoWillExecutorNotPresent,
|
||||
Will,
|
||||
WillItem,
|
||||
)
|
||||
from bal.core.willexecutors import Willexecutors
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# Load karen7 wallet data
|
||||
@@ -53,7 +59,7 @@ def build_utxos(data):
|
||||
for txid, outputs in txo.items():
|
||||
if not isinstance(outputs, dict):
|
||||
continue
|
||||
for addr, out_map in outputs.items():
|
||||
for _, out_map in outputs.items():
|
||||
if not isinstance(out_map, dict):
|
||||
continue
|
||||
for idx, info in out_map.items():
|
||||
@@ -85,7 +91,6 @@ class FakeBalWindow:
|
||||
def init_class_variables(self):
|
||||
if not self.heirs:
|
||||
raise NoHeirsException("Heirs are not defined")
|
||||
from bal.core.plugin_base import BalTimestamp
|
||||
from datetime import datetime
|
||||
self.date_to_check = BalTimestamp(self.will_settings['threshold']).to_timestamp()
|
||||
self.no_willexecutor = self.bal_plugin.NO_WILLEXECUTOR.get()
|
||||
|
||||
@@ -45,10 +45,10 @@ class _WindowsLikeDatetime(_real_datetime):
|
||||
|
||||
def main():
|
||||
plugin_base = importlib.import_module(f"{PKG}.core.plugin_base")
|
||||
BalTimestamp = plugin_base.BalTimestamp
|
||||
bt_class = plugin_base.BalTimestamp
|
||||
|
||||
# 1) Sanity: with the real (Linux 64-bit) datetime, large ts works already.
|
||||
bt = BalTimestamp(NLOCKTIME_MAX)
|
||||
bt = bt_class(NLOCKTIME_MAX)
|
||||
d = bt.to_date()
|
||||
assert isinstance(d, _real_datetime), d
|
||||
print("[OK] BalTimestamp(NLOCKTIME_MAX).to_date() works on this platform")
|
||||
@@ -59,7 +59,7 @@ def main():
|
||||
plugin_base.datetime = _WindowsLikeDatetime
|
||||
try:
|
||||
# 2a) Absolute sentinel timestamp (the exact crash path from the log).
|
||||
bt = BalTimestamp(NLOCKTIME_MAX)
|
||||
bt = bt_class(NLOCKTIME_MAX)
|
||||
d = bt.to_date() # must NOT raise OverflowError anymore
|
||||
assert d.year <= 2038, f"expected clamp to <=2038, got {d!r}"
|
||||
print("[OK] to_date(NLOCKTIME_MAX) no longer raises (clamped to INT32_MAX)")
|
||||
@@ -75,13 +75,13 @@ def main():
|
||||
print("[OK] str()/repr() on out-of-range timestamp are safe")
|
||||
|
||||
# 2d) Relative durations that overflow when added (e.g. huge 'd').
|
||||
bt_rel = BalTimestamp(f"{10 ** 9}d") # ~2.7M years -> overflow
|
||||
bt_rel = bt_class(f"{10 ** 9}d") # ~2.7M years -> overflow
|
||||
d2 = bt_rel.to_date()
|
||||
assert d2 is not None
|
||||
print("[OK] huge relative duration no longer raises")
|
||||
|
||||
# 2e) Normal values are unchanged (behaviour-preserving check).
|
||||
bt_norm = BalTimestamp("90d")
|
||||
bt_norm = bt_class("90d")
|
||||
d3 = bt_norm.to_date()
|
||||
# 90 days from now, normalised to midnight
|
||||
assert d3.hour == 0 and d3.minute == 0 and d3.second == 0
|
||||
|
||||
Reference in New Issue
Block a user