-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Check the master and slave scheduler in context with other components Two test cases are set up; one for a regular simulation with a master scheduler, sink and source. Another for a simulation with a system simulation component. A fixture sets up the simulation and yields it before the tests, tearing it down afterwards by throwing an exception and device interrupt. --------- Co-authored-by: Abigail Emery <[email protected]> Co-authored-by: DiamondJoseph <[email protected]>
- Loading branch information
1 parent
6066902
commit aef4042
Showing
4 changed files
with
222 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
- type: tickit.devices.source.Source | ||
name: external_source | ||
inputs: {} | ||
value: 42 | ||
- type: tickit.core.components.system_simulation.SystemSimulation | ||
name: internal_tickit | ||
inputs: | ||
input_1: | ||
component: external_source | ||
port: value | ||
components: | ||
- type: tickit.devices.sink.Sink | ||
name: internal_sink | ||
inputs: | ||
sink_1: | ||
component: external | ||
port: input_1 | ||
expose: | ||
output_1: | ||
component: external | ||
port: input_1 | ||
- type: tickit.devices.sink.Sink | ||
name: external_sink | ||
inputs: | ||
sink_1: | ||
component: internal_tickit | ||
port: output_1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
- type: tickit.devices.source.Source | ||
name: source | ||
inputs: {} | ||
value: 42 | ||
- type: tickit.devices.sink.Sink | ||
name: sink | ||
inputs: | ||
input: | ||
component: source | ||
port: value |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import asyncio | ||
from asyncio import AbstractEventLoop | ||
from pathlib import Path | ||
from typing import List, cast | ||
|
||
import mock | ||
import pytest | ||
import pytest_asyncio | ||
from mock import create_autospec | ||
|
||
from tickit.core.components.component import Component, ComponentConfig | ||
from tickit.core.components.device_simulation import DeviceSimulation | ||
from tickit.core.management.event_router import InverseWiring | ||
from tickit.core.management.schedulers.master import MasterScheduler | ||
from tickit.core.state_interfaces.state_interface import get_interface | ||
from tickit.core.typedefs import ComponentException, ComponentID, PortID | ||
from tickit.devices.sink import SinkDevice | ||
from tickit.utils.configuration.loading import read_configs | ||
|
||
|
||
@pytest.fixture | ||
def configs_from_yaml() -> List[ComponentConfig]: | ||
path_to_yaml = Path(__file__).parent / "configs" / "test_with_master.yaml" | ||
return read_configs(path_to_yaml) | ||
|
||
|
||
@pytest.fixture | ||
def wiring(configs_from_yaml: List[ComponentConfig]) -> InverseWiring: | ||
return InverseWiring.from_component_configs(configs_from_yaml) | ||
|
||
|
||
@pytest.fixture | ||
def components(configs_from_yaml) -> List[Component]: | ||
return [config() for config in configs_from_yaml] | ||
|
||
|
||
@pytest_asyncio.fixture | ||
async def master_scheduler( | ||
wiring: InverseWiring, | ||
components: List[Component], | ||
event_loop: AbstractEventLoop, | ||
): | ||
with mock.patch( | ||
"tickit.devices.sink.SinkDevice.update", | ||
return_value=create_autospec(SinkDevice.update), | ||
): | ||
scheduler = MasterScheduler(wiring, *get_interface("internal")) | ||
|
||
assert scheduler.running.is_set() is False | ||
run_task = event_loop.create_task( | ||
asyncio.wait( | ||
[ | ||
event_loop.create_task( | ||
component.run_forever(*get_interface("internal")) | ||
) | ||
for component in components | ||
] | ||
+ [event_loop.create_task(scheduler.run_forever())] | ||
) | ||
) | ||
|
||
yield scheduler | ||
|
||
exception_task = event_loop.create_task( | ||
scheduler.handle_component_exception( | ||
ComponentException( | ||
source=ComponentID("sim"), error=Exception(), traceback="" | ||
) | ||
) | ||
) | ||
|
||
await asyncio.gather(run_task, exception_task) | ||
assert scheduler.running.is_set() is False | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_sink_has_captured_value( | ||
components: List[Component], | ||
master_scheduler: MasterScheduler, | ||
): | ||
await asyncio.wait_for(master_scheduler.running.wait(), timeout=2.0) | ||
|
||
sink = cast(DeviceSimulation, components[1]) | ||
|
||
await asyncio.wait_for(master_scheduler.ticker.finished.wait(), timeout=2.0) | ||
|
||
sunk_value = master_scheduler.ticker.inputs[ComponentID("sink")] | ||
|
||
mocked_update = cast(mock.MagicMock, sink.device.update) | ||
mocked_update.assert_called_once_with(0, sunk_value) | ||
assert sunk_value.get(PortID("input")) == 42 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import asyncio | ||
from asyncio import AbstractEventLoop | ||
from pathlib import Path | ||
from typing import List, cast | ||
|
||
import pytest | ||
import pytest_asyncio | ||
|
||
from tickit.core.components.component import Component, ComponentConfig | ||
from tickit.core.components.device_simulation import DeviceSimulation | ||
from tickit.core.components.system_simulation import SystemSimulationComponent | ||
from tickit.core.management.event_router import InverseWiring | ||
from tickit.core.management.schedulers.master import MasterScheduler | ||
from tickit.core.state_interfaces.state_interface import get_interface | ||
from tickit.core.typedefs import ComponentException, ComponentID, PortID | ||
from tickit.utils.configuration.loading import read_configs | ||
|
||
|
||
@pytest_asyncio.fixture | ||
def configs_from_yaml() -> List[ComponentConfig]: | ||
path_to_yaml = Path(__file__).parent / "configs" / "nested.yaml" | ||
return read_configs(path_to_yaml) | ||
|
||
|
||
@pytest_asyncio.fixture | ||
def wiring(configs_from_yaml) -> InverseWiring: | ||
return InverseWiring.from_component_configs(configs_from_yaml) | ||
|
||
|
||
@pytest_asyncio.fixture | ||
def components(configs_from_yaml) -> List[Component]: | ||
return [config() for config in configs_from_yaml] | ||
|
||
|
||
@pytest_asyncio.fixture | ||
async def master_scheduler( | ||
wiring: InverseWiring, | ||
components: List[Component], | ||
event_loop: AbstractEventLoop, | ||
): | ||
scheduler = MasterScheduler(wiring, *get_interface("internal")) | ||
|
||
assert scheduler.running.is_set() is False | ||
run_task = event_loop.create_task( | ||
asyncio.wait( | ||
[ | ||
event_loop.create_task( | ||
component.run_forever(*get_interface("internal")) | ||
) | ||
for component in components | ||
] | ||
+ [event_loop.create_task(scheduler.run_forever())] | ||
) | ||
) | ||
|
||
yield scheduler | ||
|
||
exception_task = event_loop.create_task( | ||
scheduler.handle_component_exception( | ||
ComponentException( | ||
source=ComponentID("internal_tickit"), error=Exception(), traceback="" | ||
), | ||
) | ||
) | ||
interrupt_task = event_loop.create_task( | ||
scheduler.schedule_interrupt(ComponentID("external_sink")) | ||
) | ||
|
||
await asyncio.wait([run_task, exception_task, interrupt_task]) | ||
|
||
assert scheduler.running.is_set() is False | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_sink_has_captured_value( | ||
components: List[Component], | ||
master_scheduler: MasterScheduler, | ||
): | ||
source = cast(DeviceSimulation, components[0]) | ||
sim = cast(SystemSimulationComponent, components[1]) | ||
sink = cast(DeviceSimulation, components[2]) | ||
|
||
assert sink.device_inputs == {} | ||
assert source.last_outputs == {} | ||
|
||
await asyncio.wait_for(master_scheduler.running.wait(), timeout=2.0) | ||
await asyncio.wait_for(master_scheduler.ticker.finished.wait(), timeout=3.0) | ||
|
||
assert ( | ||
source.last_outputs[PortID("value")] | ||
== sim.scheduler.input_changes[PortID("input_1")] | ||
== 42 | ||
) | ||
assert sink.device_inputs == {"sink_1": 42} |