44"""Tests for the microgrid client thin wrapper."""
55
66import asyncio
7+ import contextlib
8+ from collections .abc import AsyncIterator
79
810import grpc
911import pytest
3335# pylint: disable=missing-class-docstring,no-member
3436
3537
36- class TestMicrogridGrpcClient :
37- """Tests for the microgrid client thin wrapper."""
38+ # This incrementing port is a hack to avoid the inherent flakiness of the approach of
39+ # using a real GRPC (mock) server. The server seems to stay alive for a short time after
40+ # the test is finished, which causes the next test to fail because the port is already
41+ # in use.
42+ # This is a workaround until we have a better solution.
43+ # See https://github.com/frequenz-floss/frequenz-sdk-python/issues/662
44+ _CURRENT_PORT : int = 57897
3845
39- @staticmethod
40- def create_client (port : int ) -> client .MicrogridApiClient :
41- """Create a client for the mock API server."""
42- return client .MicrogridGrpcClient (
43- grpc .aio .insecure_channel (f"[::]:{ port } " ),
44- f"[::]:{ port } " ,
45- retry_spec = LinearBackoff (interval = 0.0 , jitter = 0.05 ),
46- )
4746
48- async def test_components (self ) -> None :
49- """Test the components() method."""
47+ @contextlib .asynccontextmanager
48+ async def _gprc_server (
49+ servicer : mock_api .MockMicrogridServicer | None = None ,
50+ ) -> AsyncIterator [tuple [mock_api .MockMicrogridServicer , client .MicrogridApiClient ]]:
51+ global _CURRENT_PORT # pylint: disable=global-statement
52+ port = _CURRENT_PORT
53+ _CURRENT_PORT += 1
54+ if servicer is None :
5055 servicer = mock_api .MockMicrogridServicer ()
51- server = mock_api .MockGrpcServer (servicer , port = 57899 )
52- await server .start ()
56+ server = mock_api .MockGrpcServer (servicer , port = port )
57+ microgrid = client .MicrogridGrpcClient (
58+ grpc .aio .insecure_channel (f"[::]:{ port } " ),
59+ f"[::]:{ port } " ,
60+ retry_spec = LinearBackoff (interval = 0.0 , jitter = 0.05 ),
61+ )
62+ await server .start ()
63+ try :
64+ yield servicer , microgrid
65+ finally :
66+ assert await server .graceful_shutdown ()
67+
5368
54- try :
55- microgrid = self . create_client ( 57899 )
69+ class TestMicrogridGrpcClient :
70+ """Tests for the microgrid client thin wrapper."""
5671
72+ async def test_components (self ) -> None :
73+ """Test the components() method."""
74+ async with _gprc_server () as (servicer , microgrid ):
5775 assert set (await microgrid .components ()) == set ()
5876
5977 servicer .add_component (
@@ -165,18 +183,9 @@ async def test_components(self) -> None:
165183 Component (999 , ComponentCategory .BATTERY ),
166184 }
167185
168- finally :
169- assert await server .graceful_shutdown ()
170-
171186 async def test_connections (self ) -> None :
172187 """Test the connections() method."""
173- servicer = mock_api .MockMicrogridServicer ()
174- server = mock_api .MockGrpcServer (servicer , port = 57898 )
175- await server .start ()
176-
177- try :
178- microgrid = self .create_client (57898 )
179-
188+ async with _gprc_server () as (servicer , microgrid ):
180189 assert set (await microgrid .connections ()) == set ()
181190
182191 servicer .add_connection (0 , 0 )
@@ -320,9 +329,6 @@ async def test_connections(self) -> None:
320329 Connection (5 , 7 ),
321330 }
322331
323- finally :
324- assert await server .graceful_shutdown ()
325-
326332 async def test_bad_connections (self ) -> None :
327333 """Validate that the client does not apply connection filters itself."""
328334
@@ -341,13 +347,7 @@ def ListAllComponents(
341347 ) -> microgrid_pb .ComponentList :
342348 return microgrid_pb .ComponentList (components = self ._components )
343349
344- servicer = BadServicer ()
345- server = mock_api .MockGrpcServer (servicer , port = 57897 )
346- await server .start ()
347-
348- try :
349- microgrid = self .create_client (57897 )
350-
350+ async with _gprc_server (BadServicer ()) as (servicer , microgrid ):
351351 assert list (await microgrid .connections ()) == []
352352 for component_id in [1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ]:
353353 servicer .add_component (
@@ -391,18 +391,9 @@ def ListAllComponents(
391391 == unfiltered
392392 )
393393
394- finally :
395- assert await server .graceful_shutdown ()
396-
397394 async def test_meter_data (self ) -> None :
398395 """Test the meter_data() method."""
399- servicer = mock_api .MockMicrogridServicer ()
400- server = mock_api .MockGrpcServer (servicer , port = 57899 )
401- await server .start ()
402-
403- try :
404- microgrid = self .create_client (57899 )
405-
396+ async with _gprc_server () as (servicer , microgrid ):
406397 servicer .add_component (
407398 83 , components_pb .ComponentCategory .COMPONENT_CATEGORY_METER
408399 )
@@ -420,22 +411,13 @@ async def test_meter_data(self) -> None:
420411 peekable = (await microgrid .meter_data (83 )).into_peekable ()
421412 await asyncio .sleep (0.2 )
422413
423- finally :
424- assert await server .graceful_shutdown ()
425-
426414 latest = peekable .peek ()
427415 assert isinstance (latest , MeterData )
428416 assert latest .component_id == 83
429417
430418 async def test_battery_data (self ) -> None :
431419 """Test the battery_data() method."""
432- servicer = mock_api .MockMicrogridServicer ()
433- server = mock_api .MockGrpcServer (servicer , port = 57899 )
434- await server .start ()
435-
436- try :
437- microgrid = self .create_client (57899 )
438-
420+ async with _gprc_server () as (servicer , microgrid ):
439421 servicer .add_component (
440422 83 , components_pb .ComponentCategory .COMPONENT_CATEGORY_BATTERY
441423 )
@@ -453,22 +435,13 @@ async def test_battery_data(self) -> None:
453435 peekable = (await microgrid .battery_data (83 )).into_peekable ()
454436 await asyncio .sleep (0.2 )
455437
456- finally :
457- assert await server .graceful_shutdown ()
458-
459438 latest = peekable .peek ()
460439 assert isinstance (latest , BatteryData )
461440 assert latest .component_id == 83
462441
463442 async def test_inverter_data (self ) -> None :
464443 """Test the inverter_data() method."""
465- servicer = mock_api .MockMicrogridServicer ()
466- server = mock_api .MockGrpcServer (servicer , port = 57899 )
467- await server .start ()
468-
469- try :
470- microgrid = self .create_client (57899 )
471-
444+ async with _gprc_server () as (servicer , microgrid ):
472445 servicer .add_component (
473446 83 , components_pb .ComponentCategory .COMPONENT_CATEGORY_INVERTER
474447 )
@@ -486,22 +459,13 @@ async def test_inverter_data(self) -> None:
486459 peekable = (await microgrid .inverter_data (83 )).into_peekable ()
487460 await asyncio .sleep (0.2 )
488461
489- finally :
490- assert await server .graceful_shutdown ()
491-
492462 latest = peekable .peek ()
493463 assert isinstance (latest , InverterData )
494464 assert latest .component_id == 83
495465
496466 async def test_ev_charger_data (self ) -> None :
497467 """Test the ev_charger_data() method."""
498- servicer = mock_api .MockMicrogridServicer ()
499- server = mock_api .MockGrpcServer (servicer , port = 57899 )
500- await server .start ()
501-
502- try :
503- microgrid = self .create_client (57899 )
504-
468+ async with _gprc_server () as (servicer , microgrid ):
505469 servicer .add_component (
506470 83 , components_pb .ComponentCategory .COMPONENT_CATEGORY_EV_CHARGER
507471 )
@@ -519,23 +483,13 @@ async def test_ev_charger_data(self) -> None:
519483 peekable = (await microgrid .ev_charger_data (83 )).into_peekable ()
520484 await asyncio .sleep (0.2 )
521485
522- finally :
523- assert await server .graceful_shutdown ()
524-
525486 latest = peekable .peek ()
526487 assert isinstance (latest , EVChargerData )
527488 assert latest .component_id == 83
528489
529490 async def test_charge (self ) -> None :
530491 """Check if charge is able to charge component."""
531- servicer = mock_api .MockMicrogridServicer ()
532- server = mock_api .MockGrpcServer (servicer , port = 57899 )
533-
534- await server .start ()
535-
536- try :
537- microgrid = self .create_client (57899 )
538-
492+ async with _gprc_server () as (servicer , microgrid ):
539493 servicer .add_component (
540494 83 , components_pb .ComponentCategory .COMPONENT_CATEGORY_METER
541495 )
@@ -546,19 +500,9 @@ async def test_charge(self) -> None:
546500 assert servicer .latest_power .component_id == 83
547501 assert servicer .latest_power .power == 12
548502
549- finally :
550- assert await server .graceful_shutdown ()
551-
552503 async def test_discharge (self ) -> None :
553504 """Check if discharge is able to discharge component."""
554- servicer = mock_api .MockMicrogridServicer ()
555- server = mock_api .MockGrpcServer (servicer , port = 57899 )
556-
557- await server .start ()
558-
559- try :
560- microgrid = self .create_client (57899 )
561-
505+ async with _gprc_server () as (servicer , microgrid ):
562506 servicer .add_component (
563507 73 , components_pb .ComponentCategory .COMPONENT_CATEGORY_METER
564508 )
@@ -568,18 +512,10 @@ async def test_discharge(self) -> None:
568512 assert servicer .latest_power is not None
569513 assert servicer .latest_power .component_id == 73
570514 assert servicer .latest_power .power == - 15
571- finally :
572- assert await server .graceful_shutdown ()
573515
574516 async def test_set_bounds (self ) -> None :
575517 """Check if set_bounds is able to set bounds for component."""
576- servicer = mock_api .MockMicrogridServicer ()
577- server = mock_api .MockGrpcServer (servicer , port = 57899 )
578- await server .start ()
579-
580- try :
581- microgrid = self .create_client (57899 )
582-
518+ async with _gprc_server () as (servicer , microgrid ):
583519 servicer .add_component (
584520 38 , components_pb .ComponentCategory .COMPONENT_CATEGORY_INVERTER
585521 )
@@ -599,9 +535,6 @@ async def test_set_bounds(self) -> None:
599535 await microgrid .set_bounds (cid , - 10.0 , 2.0 )
600536 await asyncio .sleep (0.1 )
601537
602- finally :
603- assert await server .graceful_shutdown ()
604-
605538 assert len (expected_bounds ) == len (servicer .get_bounds ())
606539
607540 def sort_key (
0 commit comments