1
0

tests: properly clean-up MockLNWallets after tests finish

This commit is contained in:
SomberNight
2025-12-20 17:28:58 +00:00
parent c3e373a3b2
commit 0afd433c42
4 changed files with 75 additions and 35 deletions

View File

@@ -6,14 +6,19 @@ import tempfile
import shutil
import functools
import inspect
from typing import TYPE_CHECKING, List
import electrum
import electrum.logging
from electrum import constants
from electrum import util
from electrum.util import OldTaskGroup
from electrum.logging import Logger
from electrum.wallet import restore_wallet_from_text
if TYPE_CHECKING:
from .test_lnpeer import MockLNWallet
# Set this locally to make the test suite run faster.
# If set, unit tests that would normally test functions with multiple implementations,
@@ -64,8 +69,11 @@ class ElectrumTestCase(unittest.IsolatedAsyncioTestCase, Logger):
# or if a prior test raised during `setUp` or `asyncSetUp` and never released the lock.
raise Exception("timed out waiting for test_lock")
super().setUp()
self.electrum_path = tempfile.mkdtemp(prefix="electrum-unittest-base-")
self.unittest_base_path = tempfile.mkdtemp(prefix="electrum-unittest-base-")
self.electrum_path = os.path.join(self.unittest_base_path, "electrum")
util.make_dir(self.electrum_path)
assert util._asyncio_event_loop is None, "global event loop already set?!"
self._lnworkers_created = [] # type: List[MockLNWallet]
async def asyncSetUp(self):
await super().asyncSetUp()
@@ -75,13 +83,33 @@ class ElectrumTestCase(unittest.IsolatedAsyncioTestCase, Logger):
loop.set_debug(False)
util._asyncio_event_loop = loop
async def asyncTearDown(self):
# clean up lnworkers
async with OldTaskGroup() as group:
for lnworker in self._lnworkers_created:
await group.spawn(lnworker.stop())
self._lnworkers_created.clear()
await super().asyncTearDown()
def tearDown(self):
util.callback_mgr.clear_all_callbacks()
shutil.rmtree(self.electrum_path)
shutil.rmtree(self.unittest_base_path)
super().tearDown()
util._asyncio_event_loop = None # cleared here, at the ~last possible moment. asyncTearDown is too early.
self._test_lock.release()
def create_mock_lnwallet(
self,
*,
name: str,
has_anchors: bool,
) -> 'MockLNWallet':
from .test_lnpeer import _create_mock_lnwallet
data_dir = tempfile.mkdtemp(prefix="lnwallet-", dir=self.unittest_base_path)
lnwallet = _create_mock_lnwallet(name=name, has_anchors=has_anchors, data_dir=data_dir)
self._lnworkers_created.append(lnwallet)
return lnwallet
def as_testnet(func):
"""Function decorator to run a single unit test in testnet mode.

View File

@@ -123,8 +123,8 @@ def create_channel_state(
def create_test_channels(
*,
alice_lnwallet: 'MockLNWallet' = None,
bob_lnwallet: 'MockLNWallet' = None,
alice_lnwallet: 'MockLNWallet',
bob_lnwallet: 'MockLNWallet',
feerate=6000,
local_msat=None,
remote_msat=None,
@@ -137,12 +137,6 @@ def create_test_channels(
if random_seed is None: # needed for deterministic randomness
random_seed = os.urandom(32)
random_gen = PRNG(random_seed)
if alice_lnwallet is None:
from .test_lnpeer import create_mock_lnwallet
alice_lnwallet = create_mock_lnwallet(name="alice", has_anchors=anchor_outputs)
if bob_lnwallet is None:
from .test_lnpeer import create_mock_lnwallet
bob_lnwallet = create_mock_lnwallet(name="bob", has_anchors=anchor_outputs)
alice_name = alice_lnwallet.name
bob_name = bob_lnwallet.name
alice_pubkey = alice_lnwallet.node_keypair.pubkey
@@ -267,12 +261,21 @@ class TestFee(ElectrumTestCase):
test
https://github.com/lightningnetwork/lightning-rfc/blob/e0c436bd7a3ed6a028e1cb472908224658a14eca/03-transactions.md#requirements-2
"""
async def asyncSetUp(self):
await super().asyncSetUp()
self.alice_lnwallet = self.create_mock_lnwallet(name="alice", has_anchors=self.TEST_ANCHOR_CHANNELS)
self.bob_lnwallet = self.create_mock_lnwallet(name="bob", has_anchors=self.TEST_ANCHOR_CHANNELS)
async def test_fee(self):
alice_channel, bob_channel = create_test_channels(
feerate=253,
local_msat=10_000_000_000,
remote_msat=5_000_000_000,
anchor_outputs=self.TEST_ANCHOR_CHANNELS)
anchor_outputs=self.TEST_ANCHOR_CHANNELS,
alice_lnwallet=self.alice_lnwallet,
bob_lnwallet=self.bob_lnwallet,
)
expected_value = 9_999_056 if self.TEST_ANCHOR_CHANNELS else 9_999_817
self.assertIn(expected_value, [x.value for x in alice_channel.get_latest_commitment(LOCAL).outputs()])
@@ -297,10 +300,14 @@ class TestChannel(ElectrumTestCase):
async def asyncSetUp(self):
await super().asyncSetUp()
self.alice_lnwallet = self.create_mock_lnwallet(name="alice", has_anchors=self.TEST_ANCHOR_CHANNELS)
self.bob_lnwallet = self.create_mock_lnwallet(name="bob", has_anchors=self.TEST_ANCHOR_CHANNELS)
# Create a test channel which will be used for the duration of this
# unittest. The channel will be funded evenly with Alice having 5 BTC,
# and Bob having 5 BTC.
self.alice_channel, self.bob_channel = create_test_channels(anchor_outputs=self.TEST_ANCHOR_CHANNELS)
self.alice_channel, self.bob_channel = create_test_channels(
anchor_outputs=self.TEST_ANCHOR_CHANNELS, alice_lnwallet=self.alice_lnwallet, bob_lnwallet=self.bob_lnwallet)
self.paymentPreimage = b"\x01" * 32
paymentHash = bitcoin.sha256(self.paymentPreimage)
@@ -785,8 +792,14 @@ class TestChannelAnchors(TestChannel):
class TestAvailableToSpend(ElectrumTestCase):
async def asyncSetUp(self):
await super().asyncSetUp()
self.alice_lnwallet = self.create_mock_lnwallet(name="alice", has_anchors=self.TEST_ANCHOR_CHANNELS)
self.bob_lnwallet = self.create_mock_lnwallet(name="bob", has_anchors=self.TEST_ANCHOR_CHANNELS)
async def test_DesyncHTLCs(self):
alice_channel, bob_channel = create_test_channels(anchor_outputs=self.TEST_ANCHOR_CHANNELS)
alice_channel, bob_channel = create_test_channels(
anchor_outputs=self.TEST_ANCHOR_CHANNELS, alice_lnwallet=self.alice_lnwallet, bob_lnwallet=self.bob_lnwallet)
self.assertEqual(499986152000 if not alice_channel.has_anchors() else 499980692000, alice_channel.available_to_spend(LOCAL))
self.assertEqual(500000000000, bob_channel.available_to_spend(LOCAL))
@@ -837,7 +850,10 @@ class TestAvailableToSpend(ElectrumTestCase):
local_msat=4000000000,
remote_msat=4000000000,
local_max_inflight=1000000000,
remote_max_inflight=2000000000)
remote_max_inflight=2000000000,
alice_lnwallet=self.alice_lnwallet,
bob_lnwallet=self.bob_lnwallet,
)
# alice can send 20 but bob can only receive 10, because of stricter receiving rules
self.assertEqual(2000000000, alice_channel.available_to_spend(LOCAL))
@@ -893,8 +909,11 @@ class TestAvailableToSpendAnchors(TestAvailableToSpend):
class TestChanReserve(ElectrumTestCase):
def setUp(self):
alice_channel, bob_channel = create_test_channels(anchor_outputs=False)
async def asyncSetUp(self):
await super().asyncSetUp()
alice_lnwallet = self.create_mock_lnwallet(name="alice", has_anchors=self.TEST_ANCHOR_CHANNELS)
bob_lnwallet = self.create_mock_lnwallet(name="bob", has_anchors=self.TEST_ANCHOR_CHANNELS)
alice_channel, bob_channel = create_test_channels(anchor_outputs=False, alice_lnwallet=alice_lnwallet, bob_lnwallet=bob_lnwallet)
alice_min_reserve = int(.5 * one_bitcoin_in_msat // 1000)
# We set Bob's channel reserve to a value that is larger than
# his current balance in the channel. This will ensure that
@@ -1027,9 +1046,14 @@ class TestChanReserveAnchors(TestChanReserve):
class TestDust(ElectrumTestCase):
async def asyncSetUp(self):
await super().asyncSetUp()
self.alice_lnwallet = self.create_mock_lnwallet(name="alice", has_anchors=self.TEST_ANCHOR_CHANNELS)
self.bob_lnwallet = self.create_mock_lnwallet(name="bob", has_anchors=self.TEST_ANCHOR_CHANNELS)
async def test_DustLimit(self):
"""Test that addition of an HTLC below the dust limit changes the balances."""
alice_channel, bob_channel = create_test_channels(anchor_outputs=self.TEST_ANCHOR_CHANNELS)
alice_channel, bob_channel = create_test_channels(anchor_outputs=self.TEST_ANCHOR_CHANNELS, alice_lnwallet=self.alice_lnwallet, bob_lnwallet=self.bob_lnwallet)
dust_limit_alice = alice_channel.config[LOCAL].dust_limit_sat
dust_limit_bob = bob_channel.config[LOCAL].dust_limit_sat
self.assertLess(dust_limit_alice, dust_limit_bob)

View File

@@ -137,9 +137,8 @@ class MockStandardWallet(Standard_Wallet):
assert passphrase
return passphrase # lol, super secure name
def create_mock_lnwallet(*, name, has_anchors) -> 'MockLNWallet':
_user_dir = tempfile.mkdtemp(prefix="electrum-lnpeer-test-") # TODO clean-up after??
config = SimpleConfig({}, read_user_dir_function=lambda: _user_dir)
def _create_mock_lnwallet(*, name, has_anchors, data_dir: str) -> 'MockLNWallet':
config = SimpleConfig({}, read_user_dir_function=lambda: data_dir)
config.ENABLE_ANCHOR_CHANNELS = has_anchors
config.INITIAL_TRAMPOLINE_FEE_LEVEL = 0
@@ -154,7 +153,6 @@ def create_mock_lnwallet(*, name, has_anchors) -> 'MockLNWallet':
lnworker = wallet.lnworker
assert isinstance(lnworker, MockLNWallet), f"{lnworker=!r}"
lnworker._user_dir = _user_dir
lnworker.lnpeermgr.network = network
lnworker.logger.info(f"created LNWallet[{name}] with nodeID={lnworker.node_keypair.pubkey.hex()}")
return lnworker
@@ -410,16 +408,8 @@ class TestPeer(ElectrumTestCase):
def setUp(self):
super().setUp()
self.GRAPH_DEFINITIONS = copy.deepcopy(_GRAPH_DEFINITIONS)
self._lnworkers_created = [] # type: List[MockLNWallet]
async def asyncTearDown(self):
# clean up lnworkers
async with OldTaskGroup() as group:
for lnworker in self._lnworkers_created:
await group.spawn(lnworker.stop())
for lnworker in self._lnworkers_created:
shutil.rmtree(lnworker._user_dir)
self._lnworkers_created.clear()
electrum.trampoline._TRAMPOLINE_NODES_UNITTESTS = {}
await super().asyncTearDown()
@@ -501,8 +491,7 @@ class TestPeer(ElectrumTestCase):
def prepare_lnwallets(self, graph_definition) -> Mapping[str, MockLNWallet]:
workers = {} # type: Dict[str, MockLNWallet]
for a, definition in graph_definition.items():
workers[a] = create_mock_lnwallet(name=a, has_anchors=self.TEST_ANCHOR_CHANNELS)
self._lnworkers_created.extend(list(workers.values()))
workers[a] = self.create_mock_lnwallet(name=a, has_anchors=self.TEST_ANCHOR_CHANNELS)
return workers
def prepare_chans_and_peers_in_graph(

View File

@@ -25,7 +25,6 @@ from electrum.util import bfh, read_json_file, OldTaskGroup, get_asyncio_loop
from electrum.logging import console_stderr_handler
from . import ElectrumTestCase
from .test_lnpeer import create_mock_lnwallet
TIME_STEP = 0.01 # run tests 100 x faster
@@ -353,7 +352,7 @@ class TestOnionMessageManager(ElectrumTestCase):
async def test_request_and_reply(self):
n = MockNetwork()
lnw = create_mock_lnwallet(name='test_request_and_reply', has_anchors=False)
lnw = self.create_mock_lnwallet(name='test_request_and_reply', has_anchors=False)
def slow(*args, **kwargs):
time.sleep(2*TIME_STEP)
@@ -399,7 +398,7 @@ class TestOnionMessageManager(ElectrumTestCase):
async def test_forward(self):
n = MockNetwork()
lnw = create_mock_lnwallet(name='alice', has_anchors=False)
lnw = self.create_mock_lnwallet(name='alice', has_anchors=False)
lnw.node_keypair = self.alice
self.was_sent = False
@@ -436,7 +435,7 @@ class TestOnionMessageManager(ElectrumTestCase):
async def test_receive_unsolicited(self):
n = MockNetwork()
lnw = create_mock_lnwallet(name='dave', has_anchors=False)
lnw = self.create_mock_lnwallet(name='dave', has_anchors=False)
lnw.node_keypair = self.dave
t = OnionMessageManager(lnw)