Skip to content

Commit

Permalink
Merge pull request #525 from puddly/rc
Browse files Browse the repository at this point in the history
0.35.7 Release
  • Loading branch information
puddly authored Jan 30, 2023
2 parents 5042f2c + 60b7dc2 commit 9263eb1
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 3 deletions.
2 changes: 1 addition & 1 deletion bellows/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
MAJOR_VERSION = 0
MINOR_VERSION = 34
PATCH_VERSION = "6"
PATCH_VERSION = "7"
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__ = f"{__short_version__}.{PATCH_VERSION}"
7 changes: 7 additions & 0 deletions bellows/uart.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,13 @@ def connection_lost(self, exc):

LOGGER.debug("Connection lost: %r", exc)

# XXX: The startup reset future must be resolved with an error *before* the
# "connection done" future is completed: the secondary thread has an attached
# callback to stop itself, which will cause the a future to propagate a
# `CancelledError` into the active event loop, breaking everything!
if self._startup_reset_future:
self._startup_reset_future.set_exception(exc)

if self._connection_done_future:
self._connection_done_future.set_result(exc)
self._connection_done_future = None
Expand Down
6 changes: 6 additions & 0 deletions bellows/zigbee/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,12 @@ async def write_network_info(
await ezsp.setValue(ezsp.types.EzspValueId.VALUE_STACK_TOKEN_WRITING, 1)

async def reset_network_info(self):
# The network must be running before we can leave it
try:
await self._ensure_network_running()
except zigpy.exceptions.NetworkNotFormed:
return

try:
(status,) = await self._ezsp.leaveNetwork()
except bellows.exception.EzspError:
Expand Down
16 changes: 15 additions & 1 deletion tests/test_application_network_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,9 @@ def get_addr_table_eui64(index):

def _mock_app_for_write(app, network_info, node_info, ezsp_ver=None):
ezsp = app._ezsp

ezsp.networkState = AsyncMock(
return_value=[ezsp.types.EmberNetworkStatus.JOINED_NETWORK]
)
ezsp.leaveNetwork = AsyncMock(return_value=[t.EmberStatus.NETWORK_DOWN])
ezsp.getEui64 = AsyncMock(
return_value=[t.EmberEUI64.convert("00:12:4b:00:1c:a1:b8:46")]
Expand Down Expand Up @@ -483,3 +485,15 @@ async def test_write_network_info_generate_hashed_tclk(app, network_info, node_i

# A new hashed key is randomly generated each time if none is provided
assert len(seen_keys) == 10


async def test_reset_network_with_no_formed_network(app):
_mock_app_for_write(app, network_info, node_info)

app._ezsp.networkState = AsyncMock(
return_value=[app._ezsp.types.EmberNetworkStatus.NO_NETWORK]
)

app._ezsp.networkInit = AsyncMock(return_value=[t.EmberStatus.NOT_JOINED])

await app.reset_network_info()
41 changes: 40 additions & 1 deletion tests/test_uart.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from bellows import uart
import bellows.config as conf

from .async_mock import AsyncMock, MagicMock, sentinel
from .async_mock import AsyncMock, MagicMock, patch, sentinel


@pytest.mark.parametrize("flow_control", [conf.CONF_FLOW_CONTROL_DEFAULT, "hardware"])
Expand Down Expand Up @@ -99,6 +99,45 @@ def on_transport_close():
assert len(threads) == 0


async def test_connect_threaded_failure_cancellation_propagation(monkeypatch):
appmock = MagicMock()

async def mock_connect(loop, protocol_factory, *args, **kwargs):
protocol = protocol_factory()
transport = AsyncMock()

protocol.connection_made(transport)

return transport, protocol

with patch("bellows.uart.zigpy.serial.create_serial_connection", mock_connect):
gw = await uart.connect(
conf.SCHEMA_DEVICE(
{
conf.CONF_DEVICE_PATH: "/dev/serial",
conf.CONF_DEVICE_BAUDRATE: 115200,
}
),
appmock,
use_thread=True,
)

# Begin waiting for the startup reset
wait_for_reset = gw.wait_for_startup_reset()

# But lose connection halfway through
asyncio.get_running_loop().call_later(0.1, gw.connection_lost, RuntimeError())

# Cancellation should propagate to the outer loop
with pytest.raises(RuntimeError):
await wait_for_reset

# Ensure all threads are cleaned up
[t.join(1) for t in threading.enumerate() if "bellows" in t.name]
threads = [t for t in threading.enumerate() if "bellows" in t.name]
assert len(threads) == 0


@pytest.fixture
def gw():
gw = uart.Gateway(MagicMock())
Expand Down

0 comments on commit 9263eb1

Please sign in to comment.