Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ jobs:
env:
IN_CONTAINER: "true"
run: |
pytest --cov=tests --cov-report=xml
pytest -s -v --cov=tests --cov-report=xml

- name: Cleanup test environment
if: always()
Expand Down
4 changes: 2 additions & 2 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
[pytest]
minversion = 6.0
addopts = -v
log_level = INFO
log_level = DEBUG
log_cli = True
log_cli_level = INFO
log_cli_level = DEBUG
console_output_style = progress
testpaths =
tests
Expand Down
25 changes: 23 additions & 2 deletions src/cpp/common/py_monero_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ void PyThreadPoller::init_common(const std::string& name) {
}

void PyThreadPoller::set_is_polling(bool is_polling) {
if (is_polling == m_is_polling) return;
if (is_polling == m_is_polling) {
std::cout << "Already set " << is_polling << std::endl;
return;
}
m_is_polling = is_polling;

if (m_is_polling) {
Expand All @@ -46,12 +49,17 @@ void PyThreadPoller::set_period_in_ms(uint64_t period_ms) {
}

void PyThreadPoller::run_poll_loop() {
if (m_poll_loop_running) return; // only run one loop at a time
if (m_poll_loop_running) {
std::cout << "PyThreadPoller::run_poll_loop(): already running loop" << std::endl;
return; // only run one loop at a time
}
m_poll_loop_running = true;

// start pool loop thread
// TODO: use global threadpool, background sync wasm wallet in c++ thread
std::cout << "Initializing thread..." << std::endl;
m_thread = boost::thread([this]() {
std::cout << "Started thread" << std::endl;

// poll while enabled
while (m_is_polling) {
Expand All @@ -67,8 +75,11 @@ void PyThreadPoller::run_poll_loop() {
}
}

std::cout << "End thread" << std::endl;
m_poll_loop_running = false;
});
std::cout << "Initialized thread" << std::endl;

}

py::object PyGenUtils::convert_value(const std::string& val) {
Expand Down Expand Up @@ -753,8 +764,10 @@ void PyMoneroConnectionManager::stop_polling() {
}

void PyMoneroConnectionManager::start_polling(const boost::optional<uint64_t>& period_ms, const boost::optional<bool>& auto_switch, const boost::optional<uint64_t>& timeout_ms, const boost::optional<PyMoneroConnectionPollType>& poll_type, const boost::optional<std::vector<std::shared_ptr<PyMoneroRpcConnection>>> &excluded_connections) {
std::cout << "PyMoneroConnectionManager::start_polling()" << std::endl;
// stop polling
stop_polling();
std::cout << "PyMoneroConnectionManager::start_polling(): stopped polling" << std::endl;

// apply defaults
uint64_t poll_period_ms = period_ms == boost::none ? DEFAULT_POLL_PERIOD : period_ms.get();
Expand All @@ -770,7 +783,9 @@ void PyMoneroConnectionManager::start_polling(const boost::optional<uint64_t>& p
}

// start polling
std::cout << "PyMoneroConnectionManager::start_polling(): set polling..." << std::endl;
set_is_polling(true);
std::cout << "PyMoneroConnectionManager::start_polling(): set polling: " << m_is_polling << std::endl;
}

std::shared_ptr<PyMoneroRpcConnection> PyMoneroConnectionManager::get_best_available_connection(const std::set<std::shared_ptr<PyMoneroRpcConnection>>& excluded_connections) {
Expand Down Expand Up @@ -908,6 +923,7 @@ std::vector<std::vector<std::shared_ptr<PyMoneroRpcConnection>>> PyMoneroConnect

void PyMoneroConnectionManager::poll() {
// do polling
std::cout << "PyMoneroConnectionManager::poll()" << std::endl;
switch (m_poll_type) {
case PyMoneroConnectionPollType::CURRENT:
check_connection();
Expand All @@ -927,6 +943,7 @@ bool PyMoneroConnectionManager::check_connections(const std::vector<std::shared_
try {
// start checking connections in parallel
boost::asio::thread_pool pool(4);
std::cout << "PyMoneroConnectionManager::check_connections(): initiliazed thread pool" << std::endl;
boost::mutex result_mutex;
boost::condition_variable result_cv;
std::vector<std::shared_ptr<PyMoneroRpcConnection>> completed;
Expand All @@ -939,9 +956,11 @@ bool PyMoneroConnectionManager::check_connections(const std::vector<std::shared_
num_tasks++;

boost::asio::post(pool, [this, connection, &result_mutex, &result_cv, &completed]() {
std::cout << "checking connection: " << connection->serialize() << std::endl;
bool change = connection->check_connection(m_timeout);

if (change && connection == get_connection()) {
std::cout << "changed connection: " << connection->serialize() << std::endl;
on_connection_changed(connection);
}

Expand All @@ -960,9 +979,11 @@ bool PyMoneroConnectionManager::check_connections(const std::vector<std::shared_
// wait for responses
while (received < num_tasks) {
boost::unique_lock<boost::mutex> lock(result_mutex);
std::cout << "Waiting for connection..." << std::endl;
result_cv.wait(lock, [&]() { return completed.size() > received; });

auto connection = completed[received++];
std::cout << "Got connection: " << connection->serialize() << std::endl;
lock.unlock();

if (connection->is_connected().value_or(false) && !has_connection) {
Expand Down
10 changes: 7 additions & 3 deletions src/cpp/daemon/py_monero_daemon.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,17 @@ class PyMoneroDaemonListener : public monero_daemon_listener {

class PyMoneroBlockNotifier : public PyMoneroDaemonListener {
public:
boost::mutex* temp;
boost::condition_variable* cv;
PyMoneroBlockNotifier(boost::mutex* temp, boost::condition_variable* cv) { this->temp = temp; this->cv = cv; }
PyMoneroBlockNotifier(boost::mutex* temp, boost::condition_variable* cv, bool* ready) { this->temp = temp; this->cv = cv; this->ready = ready; }
void on_block_header(const std::shared_ptr<monero::monero_block_header>& header) override {
boost::mutex::scoped_lock lock(*temp);
m_last_header = header;
*ready = true;
cv->notify_one();
}
private:
boost::mutex* temp;
boost::condition_variable* cv;
bool* ready;
};

class PyMoneroDaemon {
Expand Down
7 changes: 4 additions & 3 deletions src/cpp/daemon/py_monero_daemon_rpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -616,19 +616,20 @@ void PyMoneroDaemonRpc::stop() {
}

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

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

// register the listener
add_listener(block_listener);

// wait until condition variable is notified
boost::mutex::scoped_lock lock(temp);
cv.wait(lock);
cv.wait(lock, [&]() { return ready; });

// unregister the listener
remove_listener(block_listener);
Expand Down
10 changes: 3 additions & 7 deletions tests/test_monero_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,15 @@
MoneroError, MoneroRpcError, SerializableStruct
)

from utils import BaseTestClass

logger: logging.Logger = logging.getLogger("TestMoneroCommon")


@pytest.mark.unit
class TestMoneroCommon:
class TestMoneroCommon(BaseTestClass):
"""Monero common unit tests"""

@pytest.fixture(autouse=True)
def setup_and_teardown(self, request: pytest.FixtureRequest):
logger.info(f"Before {request.node.name}") # type: ignore
yield
logger.info(f"After {request.node.name}") # type: ignore

# test monero error inheritance
def test_monero_error(self) -> None:
monero_err: MoneroError = MoneroError("Test monero error")
Expand Down
42 changes: 13 additions & 29 deletions tests/test_monero_connection_manager.py
Original file line number Diff line number Diff line change
@@ -1,62 +1,46 @@
import pytest
import logging

from typing import Optional
from typing import Optional, override
from monero import (
MoneroConnectionManager, MoneroRpcConnection, MoneroConnectionPollType
)
from utils import (
ConnectionChangeCollector, TestUtils as Utils,
AssertUtils, RpcConnectionUtils
AssertUtils, RpcConnectionUtils, BaseTestClass
)

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


@pytest.mark.integration
class TestMoneroConnectionManager:
class TestMoneroConnectionManager(BaseTestClass):
"""Connection manager integration tests"""

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

_cm: MoneroConnectionManager | None = None
"""Connection manager test instance."""

#region Fixtures

# Setup and teardown of test class
@pytest.fixture(scope="class", autouse=True)
def global_setup_and_teardown(self):
"""Executed once before all tests"""
self.before_all()
yield
self.after_all()

# Before all tests
@override
def before_all(self) -> None:
"""Executed once before all tests"""
logger.info(f"Setup test class {type(self).__name__}")
super().before_all()
self._cm = MoneroConnectionManager()

# After all tests
@override
def after_all(self) -> None:
"""Executed once after all tests"""
logger.info(f"Teardown test class {type(self).__name__}")
super().after_all()
if self._cm:
self._cm.reset()
logger.debug("Resetted connection manager")
else:
logger.warning("Test connection manager is not set!")

Utils.RPC_WALLET_MANAGER.clear()

# setup and teardown of each test
@pytest.fixture(autouse=True)
def setup_and_teardown(self, request: pytest.FixtureRequest):
logger.info(f"Before {request.node.name}") # type: ignore
yield
logger.info(f"After {request.node.name}") # type: ignore

# test connnections fixture
@pytest.fixture(scope="class")
def connections(self) -> list[MoneroRpcConnection]:
Expand Down Expand Up @@ -122,7 +106,7 @@ def test_connection_manager(self, connection_manager: MoneroConnectionManager, c

# auto connect to the best available connection
connection_manager.start_polling(Utils.SYNC_PERIOD_IN_MS)
listener.wait_for_change(Utils.SYNC_PERIOD_IN_MS, "Waiting for auto connect to best available connection")
listener.wait_for_change(num_expected_changes + 1, Utils.SYNC_PERIOD_IN_MS, "Waiting for auto connect to best available connection")
assert connection_manager.is_connected()
connection = connection_manager.get_connection()
assert connection is not None
Expand Down Expand Up @@ -166,7 +150,7 @@ def test_connection_manager(self, connection_manager: MoneroConnectionManager, c
continue
conn.proxy_uri = self.OFFLINE_PROXY_URI

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

Expand Down Expand Up @@ -330,7 +314,7 @@ def test_connection_manager(self, connection_manager: MoneroConnectionManager, c
poll_type=MoneroConnectionPollType.CURRENT
)

listener.wait_for_change(Utils.SYNC_PERIOD_IN_MS, "Polling current connection")
listener.wait_for_change(num_expected_changes + 1, Utils.SYNC_PERIOD_IN_MS, "Polling current connection")
assert connection_manager.is_connected() is True
num_expected_changes += 1
assert num_expected_changes == listener.num_changed_connections
Expand All @@ -340,7 +324,7 @@ def test_connection_manager(self, connection_manager: MoneroConnectionManager, c
num_expected_changes += 1
assert num_expected_changes == listener.num_changed_connections
connection_manager.start_polling(period_ms=Utils.SYNC_PERIOD_IN_MS, poll_type=MoneroConnectionPollType.ALL)
listener.wait_for_change(Utils.SYNC_PERIOD_IN_MS, "Polling all connections")
listener.wait_for_change(num_expected_changes + 1, Utils.SYNC_PERIOD_IN_MS, "Polling all connections")
assert connection_manager.is_connected() is True
num_expected_changes += 1
assert num_expected_changes == listener.num_changed_connections
Expand All @@ -351,7 +335,7 @@ def test_connection_manager(self, connection_manager: MoneroConnectionManager, c
for con in ordered_connections:
con.proxy_uri = self.OFFLINE_PROXY_URI

listener.wait_for_change(Utils.SYNC_PERIOD_IN_MS, "Simulating total shut down")
listener.wait_for_change(num_expected_changes + 1, Utils.SYNC_PERIOD_IN_MS, "Simulating total shut down")
assert connection.is_online() is False, f"Expected offline connection: {connection.serialize()}"
num_expected_changes += 1
assert num_expected_changes == listener.num_changed_connections
Expand Down
9 changes: 2 additions & 7 deletions tests/test_monero_daemon_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,16 @@
import logging

from monero import MoneroDaemon, MoneroBan
from utils import BaseTestClass

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


# Test calls to MoneroDaemon interface
@pytest.mark.unit
class TestMoneroDaemonInterface:
class TestMoneroDaemonInterface(BaseTestClass):
"""Daemon interface bindings unit tests"""

@pytest.fixture(autouse=True)
def setup_and_teardown(self, request: pytest.FixtureRequest):
logger.info(f"Before {request.node.name}") # type: ignore
yield
logger.info(f"After {request.node.name}") # type: ignore

@pytest.fixture(scope="class")
def daemon(self) -> MoneroDaemon:
"""Test daemon interface instance"""
Expand Down
15 changes: 6 additions & 9 deletions tests/test_monero_daemon_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import time
import logging

from typing import override

from monero import (
MoneroDaemonRpc, MoneroVersion, MoneroBlockHeader, MoneroBlockTemplate,
MoneroBlock, MoneroMiningStatus, MoneroPruneResult,
Expand All @@ -20,30 +22,25 @@
BlockUtils, GenUtils,
DaemonUtils, WalletType,
IntegrationTestUtils,
SubmitThenRelayTxTester
SubmitThenRelayTxTester, BaseTestClass
)

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


@pytest.mark.integration
class TestMoneroDaemonRpc:
class TestMoneroDaemonRpc(BaseTestClass):
"""Rpc daemon integration tests"""
BINARY_BLOCK_CTX: BinaryBlockContext = BinaryBlockContext()
_test_wallet: MoneroWalletRpc | None = None

#region Fixtures

@pytest.fixture(scope="class", autouse=True)
@override
def before_all(self):
# setup wallet rpc for tests
IntegrationTestUtils.setup(WalletType.RPC)

@pytest.fixture(autouse=True)
def setup_and_teardown(self, request: pytest.FixtureRequest):
logger.info(f"Before {request.node.name}") # type: ignore
yield
logger.info(f"After {request.node.name}") # type: ignore

@pytest.fixture(scope="class")
def daemon(self) -> MoneroDaemonRpc:
"""Test rpc daemon instance"""
Expand Down
Loading
Loading