Skip to content

Commit de363a7

Browse files
Minor fixes and refactory
* Refactory test fixtures * Fix potential notification loss and deadlock * Code quality fixes
1 parent 3238b37 commit de363a7

22 files changed

Lines changed: 199 additions & 210 deletions

src/cpp/daemon/py_monero_daemon.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,17 @@ class PyMoneroDaemonListener : public monero_daemon_listener {
2121

2222
class PyMoneroBlockNotifier : public PyMoneroDaemonListener {
2323
public:
24-
boost::mutex* temp;
25-
boost::condition_variable* cv;
26-
PyMoneroBlockNotifier(boost::mutex* temp, boost::condition_variable* cv) { this->temp = temp; this->cv = cv; }
24+
PyMoneroBlockNotifier(boost::mutex* temp, boost::condition_variable* cv, bool* ready) { this->temp = temp; this->cv = cv; this->ready = ready; }
2725
void on_block_header(const std::shared_ptr<monero::monero_block_header>& header) override {
26+
boost::mutex::scoped_lock lock(*temp);
2827
m_last_header = header;
28+
*ready = true;
2929
cv->notify_one();
3030
}
31+
private:
32+
boost::mutex* temp;
33+
boost::condition_variable* cv;
34+
bool* ready;
3135
};
3236

3337
class PyMoneroDaemon {

src/cpp/daemon/py_monero_daemon_rpc.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -616,19 +616,20 @@ void PyMoneroDaemonRpc::stop() {
616616
}
617617

618618
std::shared_ptr<monero::monero_block_header> PyMoneroDaemonRpc::wait_for_next_block_header() {
619-
// use mutex and condition variable to wait for block
619+
// use mutex and condition variable with predicate to wait for block
620620
boost::mutex temp;
621621
boost::condition_variable cv;
622+
bool ready = false;
622623

623624
// create listener which notifies condition variable when block is added
624-
auto block_listener = std::make_shared<PyMoneroBlockNotifier>(&temp, &cv);
625+
auto block_listener = std::make_shared<PyMoneroBlockNotifier>(&temp, &cv, &ready);
625626

626627
// register the listener
627628
add_listener(block_listener);
628629

629630
// wait until condition variable is notified
630631
boost::mutex::scoped_lock lock(temp);
631-
cv.wait(lock);
632+
cv.wait(lock, [&]() { return ready; });
632633

633634
// unregister the listener
634635
remove_listener(block_listener);

tests/test_monero_common.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,15 @@
55
MoneroError, MoneroRpcError, SerializableStruct
66
)
77

8+
from utils import BaseTestClass
9+
810
logger: logging.Logger = logging.getLogger("TestMoneroCommon")
911

1012

1113
@pytest.mark.unit
12-
class TestMoneroCommon:
14+
class TestMoneroCommon(BaseTestClass):
1315
"""Monero common unit tests"""
1416

15-
@pytest.fixture(autouse=True)
16-
def setup_and_teardown(self, request: pytest.FixtureRequest):
17-
logger.info(f"Before {request.node.name}") # type: ignore
18-
yield
19-
logger.info(f"After {request.node.name}") # type: ignore
20-
2117
# test monero error inheritance
2218
def test_monero_error(self) -> None:
2319
monero_err: MoneroError = MoneroError("Test monero error")

tests/test_monero_connection_manager.py

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,46 @@
11
import pytest
22
import logging
33

4-
from typing import Optional
4+
from typing import Optional, override
55
from monero import (
66
MoneroConnectionManager, MoneroRpcConnection, MoneroConnectionPollType
77
)
88
from utils import (
99
ConnectionChangeCollector, TestUtils as Utils,
10-
AssertUtils, RpcConnectionUtils
10+
AssertUtils, RpcConnectionUtils, BaseTestClass
1111
)
1212

1313
logger: logging.Logger = logging.getLogger("TestMoneroConnectionManager")
1414

1515

1616
@pytest.mark.integration
17-
class TestMoneroConnectionManager:
17+
class TestMoneroConnectionManager(BaseTestClass):
1818
"""Connection manager integration tests"""
1919

2020
OFFLINE_PROXY_URI: str = "127.0.0.1:9050"
2121
"""Proxy used to simulate offline servers"""
2222

2323
_cm: MoneroConnectionManager | None = None
24+
"""Connection manager test instance."""
2425

2526
#region Fixtures
2627

27-
# Setup and teardown of test class
28-
@pytest.fixture(scope="class", autouse=True)
29-
def global_setup_and_teardown(self):
30-
"""Executed once before all tests"""
31-
self.before_all()
32-
yield
33-
self.after_all()
34-
3528
# Before all tests
29+
@override
3630
def before_all(self) -> None:
37-
"""Executed once before all tests"""
38-
logger.info(f"Setup test class {type(self).__name__}")
31+
super().before_all()
3932
self._cm = MoneroConnectionManager()
4033

4134
# After all tests
35+
@override
4236
def after_all(self) -> None:
43-
"""Executed once after all tests"""
44-
logger.info(f"Teardown test class {type(self).__name__}")
37+
super().after_all()
4538
if self._cm:
4639
self._cm.reset()
4740
logger.debug("Resetted connection manager")
48-
else:
49-
logger.warning("Test connection manager is not set!")
5041

5142
Utils.RPC_WALLET_MANAGER.clear()
5243

53-
# setup and teardown of each test
54-
@pytest.fixture(autouse=True)
55-
def setup_and_teardown(self, request: pytest.FixtureRequest):
56-
logger.info(f"Before {request.node.name}") # type: ignore
57-
yield
58-
logger.info(f"After {request.node.name}") # type: ignore
59-
6044
# test connnections fixture
6145
@pytest.fixture(scope="class")
6246
def connections(self) -> list[MoneroRpcConnection]:
@@ -122,7 +106,7 @@ def test_connection_manager(self, connection_manager: MoneroConnectionManager, c
122106

123107
# auto connect to the best available connection
124108
connection_manager.start_polling(Utils.SYNC_PERIOD_IN_MS)
125-
listener.wait_for_change(Utils.SYNC_PERIOD_IN_MS, "Waiting for auto connect to best available connection")
109+
listener.wait_for_change(num_expected_changes + 1, Utils.SYNC_PERIOD_IN_MS, "Waiting for auto connect to best available connection")
126110
assert connection_manager.is_connected()
127111
connection = connection_manager.get_connection()
128112
assert connection is not None
@@ -166,7 +150,7 @@ def test_connection_manager(self, connection_manager: MoneroConnectionManager, c
166150
continue
167151
conn.proxy_uri = self.OFFLINE_PROXY_URI
168152

169-
listener.wait_for_change(Utils.SYNC_PERIOD_IN_MS, "Simulating priotizized servers shut down")
153+
listener.wait_for_change(num_expected_changes + 1, Utils.SYNC_PERIOD_IN_MS, "Simulating priotizized servers shut down")
170154
assert connection_manager.is_connected() is False, f"{connection_manager.get_connection().serialize()}"
171155
connection = connection_manager.get_connection()
172156

@@ -330,7 +314,7 @@ def test_connection_manager(self, connection_manager: MoneroConnectionManager, c
330314
poll_type=MoneroConnectionPollType.CURRENT
331315
)
332316

333-
listener.wait_for_change(Utils.SYNC_PERIOD_IN_MS, "Polling current connection")
317+
listener.wait_for_change(num_expected_changes + 1, Utils.SYNC_PERIOD_IN_MS, "Polling current connection")
334318
assert connection_manager.is_connected() is True
335319
num_expected_changes += 1
336320
assert num_expected_changes == listener.num_changed_connections
@@ -340,7 +324,7 @@ def test_connection_manager(self, connection_manager: MoneroConnectionManager, c
340324
num_expected_changes += 1
341325
assert num_expected_changes == listener.num_changed_connections
342326
connection_manager.start_polling(period_ms=Utils.SYNC_PERIOD_IN_MS, poll_type=MoneroConnectionPollType.ALL)
343-
listener.wait_for_change(Utils.SYNC_PERIOD_IN_MS, "Polling all connections")
327+
listener.wait_for_change(num_expected_changes + 1, Utils.SYNC_PERIOD_IN_MS, "Polling all connections")
344328
assert connection_manager.is_connected() is True
345329
num_expected_changes += 1
346330
assert num_expected_changes == listener.num_changed_connections
@@ -351,7 +335,7 @@ def test_connection_manager(self, connection_manager: MoneroConnectionManager, c
351335
for con in ordered_connections:
352336
con.proxy_uri = self.OFFLINE_PROXY_URI
353337

354-
listener.wait_for_change(Utils.SYNC_PERIOD_IN_MS, "Simulating total shut down")
338+
listener.wait_for_change(num_expected_changes + 1, Utils.SYNC_PERIOD_IN_MS, "Simulating total shut down")
355339
assert connection.is_online() is False, f"Expected offline connection: {connection.serialize()}"
356340
num_expected_changes += 1
357341
assert num_expected_changes == listener.num_changed_connections

tests/test_monero_daemon_interface.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,16 @@
22
import logging
33

44
from monero import MoneroDaemon, MoneroBan
5+
from utils import BaseTestClass
56

67
logger: logging.Logger = logging.getLogger("TestMoneroDaemonInterface")
78

89

910
# Test calls to MoneroDaemon interface
1011
@pytest.mark.unit
11-
class TestMoneroDaemonInterface:
12+
class TestMoneroDaemonInterface(BaseTestClass):
1213
"""Daemon interface bindings unit tests"""
1314

14-
@pytest.fixture(autouse=True)
15-
def setup_and_teardown(self, request: pytest.FixtureRequest):
16-
logger.info(f"Before {request.node.name}") # type: ignore
17-
yield
18-
logger.info(f"After {request.node.name}") # type: ignore
19-
2015
@pytest.fixture(scope="class")
2116
def daemon(self) -> MoneroDaemon:
2217
"""Test daemon interface instance"""

tests/test_monero_daemon_rpc.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import time
33
import logging
44

5+
from typing import override
6+
57
from monero import (
68
MoneroDaemonRpc, MoneroVersion, MoneroBlockHeader, MoneroBlockTemplate,
79
MoneroBlock, MoneroMiningStatus, MoneroPruneResult,
@@ -20,30 +22,25 @@
2022
BlockUtils, GenUtils,
2123
DaemonUtils, WalletType,
2224
IntegrationTestUtils,
23-
SubmitThenRelayTxTester
25+
SubmitThenRelayTxTester, BaseTestClass
2426
)
2527

2628
logger: logging.Logger = logging.getLogger("TestMoneroDaemonRpc")
2729

2830

2931
@pytest.mark.integration
30-
class TestMoneroDaemonRpc:
32+
class TestMoneroDaemonRpc(BaseTestClass):
3133
"""Rpc daemon integration tests"""
3234
BINARY_BLOCK_CTX: BinaryBlockContext = BinaryBlockContext()
3335
_test_wallet: MoneroWalletRpc | None = None
3436

3537
#region Fixtures
3638

37-
@pytest.fixture(scope="class", autouse=True)
39+
@override
3840
def before_all(self):
41+
# setup wallet rpc for tests
3942
IntegrationTestUtils.setup(WalletType.RPC)
4043

41-
@pytest.fixture(autouse=True)
42-
def setup_and_teardown(self, request: pytest.FixtureRequest):
43-
logger.info(f"Before {request.node.name}") # type: ignore
44-
yield
45-
logger.info(f"After {request.node.name}") # type: ignore
46-
4744
@pytest.fixture(scope="class")
4845
def daemon(self) -> MoneroDaemonRpc:
4946
"""Test rpc daemon instance"""

tests/test_monero_rpc_connection.py

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,41 +5,20 @@
55
MoneroRpcConnection, MoneroConnectionType, MoneroRpcError,
66
MoneroUtils, MoneroConnectionProriotyComparator
77
)
8-
from utils import TestUtils as Utils, DaemonUtils, StringUtils
8+
from utils import TestUtils as Utils, DaemonUtils, StringUtils, BaseTestClass
99

1010
logger: logging.Logger = logging.getLogger("TestMoneroRpcConnection")
1111

1212

1313
@pytest.mark.integration
14-
class TestMoneroRpcConnection:
14+
class TestMoneroRpcConnection(BaseTestClass):
1515
"""Rpc connection integration tests"""
1616

1717
TIMEOUT_MS: int = Utils.AUTO_CONNECT_TIMEOUT_MS * 5
1818
"""Rpc connection timeout in milliseconds."""
1919

2020
# region Fixtures
2121

22-
# Setup and teardown of test class
23-
@pytest.fixture(scope="class", autouse=True)
24-
def global_setup_and_teardown(self):
25-
"""Executed once before all tests"""
26-
logger.info(f"Setup test class {type(self).__name__}")
27-
self.before_all()
28-
yield
29-
logger.info(f"Teardown test class {type(self).__name__}")
30-
31-
# Before all tests
32-
def before_all(self) -> None:
33-
"""Executed once before all tests"""
34-
logger.info(f"Setup test class {type(self).__name__}")
35-
36-
# Setup and teardown of each tests
37-
@pytest.fixture(autouse=True)
38-
def setup_and_teardown(self, request: pytest.FixtureRequest):
39-
logger.info(f"Before {request.node.name}") # type: ignore
40-
yield
41-
logger.info(f"After {request.node.name}") # type: ignore
42-
4322
# Node rpc connection fixture
4423
@pytest.fixture(scope="class")
4524
def node_connection(self) -> MoneroRpcConnection:

tests/test_monero_utils.py

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88
from monero import (
99
MoneroNetworkType, MoneroIntegratedAddress, MoneroUtils, MoneroTxConfig
1010
)
11-
from utils import AddressBook, KeysBook, WalletUtils
11+
from utils import AddressBook, KeysBook, WalletUtils, BaseTestClass
1212

1313
logger: logging.Logger = logging.getLogger("TestMoneroUtils")
1414

1515

1616
@pytest.mark.unit
17-
class TestMoneroUtils:
17+
class TestMoneroUtils(BaseTestClass):
1818
"""Monero utilities unit tests"""
1919

2020
class Config:
@@ -59,22 +59,6 @@ def config(self) -> TestMoneroUtils.Config:
5959
parser.read('tests/config/test_monero_utils.ini')
6060
return TestMoneroUtils.Config.parse(parser)
6161

62-
# Setup and teardown of test class
63-
@pytest.fixture(scope="class", autouse=True)
64-
def global_setup_and_teardown(self):
65-
"""Executed once before all tests"""
66-
logger.info(f"Setup test class {type(self).__name__}")
67-
yield
68-
logger.info(f"Teardown test class {type(self).__name__}")
69-
70-
# Setup and teardown of each tests
71-
@pytest.fixture(autouse=True)
72-
def setup_and_teardown(self, request: pytest.FixtureRequest):
73-
"""Executed once before each test"""
74-
logger.info(f"Before {request.node.name}") # type: ignore
75-
yield
76-
logger.info(f"After {request.node.name}") # type: ignore
77-
7862
#endregion
7963

8064
#region Tests

0 commit comments

Comments
 (0)