tests: properly clean-up MockLNWallets after tests finish
This commit is contained in:
@@ -6,14 +6,19 @@ import tempfile
|
|||||||
import shutil
|
import shutil
|
||||||
import functools
|
import functools
|
||||||
import inspect
|
import inspect
|
||||||
|
from typing import TYPE_CHECKING, List
|
||||||
|
|
||||||
import electrum
|
import electrum
|
||||||
import electrum.logging
|
import electrum.logging
|
||||||
from electrum import constants
|
from electrum import constants
|
||||||
from electrum import util
|
from electrum import util
|
||||||
|
from electrum.util import OldTaskGroup
|
||||||
from electrum.logging import Logger
|
from electrum.logging import Logger
|
||||||
from electrum.wallet import restore_wallet_from_text
|
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.
|
# Set this locally to make the test suite run faster.
|
||||||
# If set, unit tests that would normally test functions with multiple implementations,
|
# 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.
|
# or if a prior test raised during `setUp` or `asyncSetUp` and never released the lock.
|
||||||
raise Exception("timed out waiting for test_lock")
|
raise Exception("timed out waiting for test_lock")
|
||||||
super().setUp()
|
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?!"
|
assert util._asyncio_event_loop is None, "global event loop already set?!"
|
||||||
|
self._lnworkers_created = [] # type: List[MockLNWallet]
|
||||||
|
|
||||||
async def asyncSetUp(self):
|
async def asyncSetUp(self):
|
||||||
await super().asyncSetUp()
|
await super().asyncSetUp()
|
||||||
@@ -75,13 +83,33 @@ class ElectrumTestCase(unittest.IsolatedAsyncioTestCase, Logger):
|
|||||||
loop.set_debug(False)
|
loop.set_debug(False)
|
||||||
util._asyncio_event_loop = loop
|
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):
|
def tearDown(self):
|
||||||
util.callback_mgr.clear_all_callbacks()
|
util.callback_mgr.clear_all_callbacks()
|
||||||
shutil.rmtree(self.electrum_path)
|
shutil.rmtree(self.unittest_base_path)
|
||||||
super().tearDown()
|
super().tearDown()
|
||||||
util._asyncio_event_loop = None # cleared here, at the ~last possible moment. asyncTearDown is too early.
|
util._asyncio_event_loop = None # cleared here, at the ~last possible moment. asyncTearDown is too early.
|
||||||
self._test_lock.release()
|
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):
|
def as_testnet(func):
|
||||||
"""Function decorator to run a single unit test in testnet mode.
|
"""Function decorator to run a single unit test in testnet mode.
|
||||||
|
|||||||
@@ -123,8 +123,8 @@ def create_channel_state(
|
|||||||
|
|
||||||
def create_test_channels(
|
def create_test_channels(
|
||||||
*,
|
*,
|
||||||
alice_lnwallet: 'MockLNWallet' = None,
|
alice_lnwallet: 'MockLNWallet',
|
||||||
bob_lnwallet: 'MockLNWallet' = None,
|
bob_lnwallet: 'MockLNWallet',
|
||||||
feerate=6000,
|
feerate=6000,
|
||||||
local_msat=None,
|
local_msat=None,
|
||||||
remote_msat=None,
|
remote_msat=None,
|
||||||
@@ -137,12 +137,6 @@ def create_test_channels(
|
|||||||
if random_seed is None: # needed for deterministic randomness
|
if random_seed is None: # needed for deterministic randomness
|
||||||
random_seed = os.urandom(32)
|
random_seed = os.urandom(32)
|
||||||
random_gen = PRNG(random_seed)
|
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
|
alice_name = alice_lnwallet.name
|
||||||
bob_name = bob_lnwallet.name
|
bob_name = bob_lnwallet.name
|
||||||
alice_pubkey = alice_lnwallet.node_keypair.pubkey
|
alice_pubkey = alice_lnwallet.node_keypair.pubkey
|
||||||
@@ -267,12 +261,21 @@ class TestFee(ElectrumTestCase):
|
|||||||
test
|
test
|
||||||
https://github.com/lightningnetwork/lightning-rfc/blob/e0c436bd7a3ed6a028e1cb472908224658a14eca/03-transactions.md#requirements-2
|
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):
|
async def test_fee(self):
|
||||||
alice_channel, bob_channel = create_test_channels(
|
alice_channel, bob_channel = create_test_channels(
|
||||||
feerate=253,
|
feerate=253,
|
||||||
local_msat=10_000_000_000,
|
local_msat=10_000_000_000,
|
||||||
remote_msat=5_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
|
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()])
|
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):
|
async def asyncSetUp(self):
|
||||||
await super().asyncSetUp()
|
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
|
# 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,
|
# unittest. The channel will be funded evenly with Alice having 5 BTC,
|
||||||
# and Bob 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
|
self.paymentPreimage = b"\x01" * 32
|
||||||
paymentHash = bitcoin.sha256(self.paymentPreimage)
|
paymentHash = bitcoin.sha256(self.paymentPreimage)
|
||||||
@@ -785,8 +792,14 @@ class TestChannelAnchors(TestChannel):
|
|||||||
|
|
||||||
|
|
||||||
class TestAvailableToSpend(ElectrumTestCase):
|
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):
|
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(499986152000 if not alice_channel.has_anchors() else 499980692000, alice_channel.available_to_spend(LOCAL))
|
||||||
self.assertEqual(500000000000, bob_channel.available_to_spend(LOCAL))
|
self.assertEqual(500000000000, bob_channel.available_to_spend(LOCAL))
|
||||||
|
|
||||||
@@ -837,7 +850,10 @@ class TestAvailableToSpend(ElectrumTestCase):
|
|||||||
local_msat=4000000000,
|
local_msat=4000000000,
|
||||||
remote_msat=4000000000,
|
remote_msat=4000000000,
|
||||||
local_max_inflight=1000000000,
|
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
|
# alice can send 20 but bob can only receive 10, because of stricter receiving rules
|
||||||
self.assertEqual(2000000000, alice_channel.available_to_spend(LOCAL))
|
self.assertEqual(2000000000, alice_channel.available_to_spend(LOCAL))
|
||||||
@@ -893,8 +909,11 @@ class TestAvailableToSpendAnchors(TestAvailableToSpend):
|
|||||||
|
|
||||||
|
|
||||||
class TestChanReserve(ElectrumTestCase):
|
class TestChanReserve(ElectrumTestCase):
|
||||||
def setUp(self):
|
async def asyncSetUp(self):
|
||||||
alice_channel, bob_channel = create_test_channels(anchor_outputs=False)
|
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)
|
alice_min_reserve = int(.5 * one_bitcoin_in_msat // 1000)
|
||||||
# We set Bob's channel reserve to a value that is larger than
|
# We set Bob's channel reserve to a value that is larger than
|
||||||
# his current balance in the channel. This will ensure that
|
# his current balance in the channel. This will ensure that
|
||||||
@@ -1027,9 +1046,14 @@ class TestChanReserveAnchors(TestChanReserve):
|
|||||||
|
|
||||||
|
|
||||||
class TestDust(ElectrumTestCase):
|
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):
|
async def test_DustLimit(self):
|
||||||
"""Test that addition of an HTLC below the dust limit changes the balances."""
|
"""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_alice = alice_channel.config[LOCAL].dust_limit_sat
|
||||||
dust_limit_bob = bob_channel.config[LOCAL].dust_limit_sat
|
dust_limit_bob = bob_channel.config[LOCAL].dust_limit_sat
|
||||||
self.assertLess(dust_limit_alice, dust_limit_bob)
|
self.assertLess(dust_limit_alice, dust_limit_bob)
|
||||||
|
|||||||
@@ -137,9 +137,8 @@ class MockStandardWallet(Standard_Wallet):
|
|||||||
assert passphrase
|
assert passphrase
|
||||||
return passphrase # lol, super secure name
|
return passphrase # lol, super secure name
|
||||||
|
|
||||||
def create_mock_lnwallet(*, name, has_anchors) -> 'MockLNWallet':
|
def _create_mock_lnwallet(*, name, has_anchors, data_dir: str) -> 'MockLNWallet':
|
||||||
_user_dir = tempfile.mkdtemp(prefix="electrum-lnpeer-test-") # TODO clean-up after??
|
config = SimpleConfig({}, read_user_dir_function=lambda: data_dir)
|
||||||
config = SimpleConfig({}, read_user_dir_function=lambda: _user_dir)
|
|
||||||
config.ENABLE_ANCHOR_CHANNELS = has_anchors
|
config.ENABLE_ANCHOR_CHANNELS = has_anchors
|
||||||
config.INITIAL_TRAMPOLINE_FEE_LEVEL = 0
|
config.INITIAL_TRAMPOLINE_FEE_LEVEL = 0
|
||||||
|
|
||||||
@@ -154,7 +153,6 @@ def create_mock_lnwallet(*, name, has_anchors) -> 'MockLNWallet':
|
|||||||
|
|
||||||
lnworker = wallet.lnworker
|
lnworker = wallet.lnworker
|
||||||
assert isinstance(lnworker, MockLNWallet), f"{lnworker=!r}"
|
assert isinstance(lnworker, MockLNWallet), f"{lnworker=!r}"
|
||||||
lnworker._user_dir = _user_dir
|
|
||||||
lnworker.lnpeermgr.network = network
|
lnworker.lnpeermgr.network = network
|
||||||
lnworker.logger.info(f"created LNWallet[{name}] with nodeID={lnworker.node_keypair.pubkey.hex()}")
|
lnworker.logger.info(f"created LNWallet[{name}] with nodeID={lnworker.node_keypair.pubkey.hex()}")
|
||||||
return lnworker
|
return lnworker
|
||||||
@@ -410,16 +408,8 @@ class TestPeer(ElectrumTestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
self.GRAPH_DEFINITIONS = copy.deepcopy(_GRAPH_DEFINITIONS)
|
self.GRAPH_DEFINITIONS = copy.deepcopy(_GRAPH_DEFINITIONS)
|
||||||
self._lnworkers_created = [] # type: List[MockLNWallet]
|
|
||||||
|
|
||||||
async def asyncTearDown(self):
|
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 = {}
|
electrum.trampoline._TRAMPOLINE_NODES_UNITTESTS = {}
|
||||||
await super().asyncTearDown()
|
await super().asyncTearDown()
|
||||||
|
|
||||||
@@ -501,8 +491,7 @@ class TestPeer(ElectrumTestCase):
|
|||||||
def prepare_lnwallets(self, graph_definition) -> Mapping[str, MockLNWallet]:
|
def prepare_lnwallets(self, graph_definition) -> Mapping[str, MockLNWallet]:
|
||||||
workers = {} # type: Dict[str, MockLNWallet]
|
workers = {} # type: Dict[str, MockLNWallet]
|
||||||
for a, definition in graph_definition.items():
|
for a, definition in graph_definition.items():
|
||||||
workers[a] = create_mock_lnwallet(name=a, has_anchors=self.TEST_ANCHOR_CHANNELS)
|
workers[a] = self.create_mock_lnwallet(name=a, has_anchors=self.TEST_ANCHOR_CHANNELS)
|
||||||
self._lnworkers_created.extend(list(workers.values()))
|
|
||||||
return workers
|
return workers
|
||||||
|
|
||||||
def prepare_chans_and_peers_in_graph(
|
def prepare_chans_and_peers_in_graph(
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ from electrum.util import bfh, read_json_file, OldTaskGroup, get_asyncio_loop
|
|||||||
from electrum.logging import console_stderr_handler
|
from electrum.logging import console_stderr_handler
|
||||||
|
|
||||||
from . import ElectrumTestCase
|
from . import ElectrumTestCase
|
||||||
from .test_lnpeer import create_mock_lnwallet
|
|
||||||
|
|
||||||
|
|
||||||
TIME_STEP = 0.01 # run tests 100 x faster
|
TIME_STEP = 0.01 # run tests 100 x faster
|
||||||
@@ -353,7 +352,7 @@ class TestOnionMessageManager(ElectrumTestCase):
|
|||||||
|
|
||||||
async def test_request_and_reply(self):
|
async def test_request_and_reply(self):
|
||||||
n = MockNetwork()
|
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):
|
def slow(*args, **kwargs):
|
||||||
time.sleep(2*TIME_STEP)
|
time.sleep(2*TIME_STEP)
|
||||||
@@ -399,7 +398,7 @@ class TestOnionMessageManager(ElectrumTestCase):
|
|||||||
|
|
||||||
async def test_forward(self):
|
async def test_forward(self):
|
||||||
n = MockNetwork()
|
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
|
lnw.node_keypair = self.alice
|
||||||
|
|
||||||
self.was_sent = False
|
self.was_sent = False
|
||||||
@@ -436,7 +435,7 @@ class TestOnionMessageManager(ElectrumTestCase):
|
|||||||
|
|
||||||
async def test_receive_unsolicited(self):
|
async def test_receive_unsolicited(self):
|
||||||
n = MockNetwork()
|
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
|
lnw.node_keypair = self.dave
|
||||||
|
|
||||||
t = OnionMessageManager(lnw)
|
t = OnionMessageManager(lnw)
|
||||||
|
|||||||
Reference in New Issue
Block a user