Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add orderer plugin #37

Merged
merged 3 commits into from
Sep 18, 2022
Merged
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
Empty file.
57 changes: 57 additions & 0 deletions tests/plugins/orderer/_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import random
from argparse import Namespace
from contextlib import contextmanager
from pathlib import Path

import pytest

from vedro import Scenario
from vedro.core import ArgumentParser, ConfigType, Dispatcher, VirtualScenario
from vedro.events import ArgParsedEvent, ArgParseEvent, ConfigLoadedEvent
from vedro.plugins.orderer import Orderer, OrdererPlugin


@pytest.fixture()
def dispatcher() -> Dispatcher:
return Dispatcher()


@pytest.fixture()
def orderer(dispatcher: Dispatcher) -> OrdererPlugin:
orderer = OrdererPlugin(Orderer)
orderer.subscribe(dispatcher)
return orderer


@contextmanager
def seeded(seed: str):
state = random.getstate()
random.seed(seed)
yield random.seed(seed)
random.setstate(state)


def make_vscenario(path: str) -> VirtualScenario:
class _Scenario(Scenario):
__file__ = Path(path).absolute()

return VirtualScenario(_Scenario, steps=[])


async def fire_config_loaded_event(dispatcher: Dispatcher, config: ConfigType) -> None:
config_loaded_event = ConfigLoadedEvent(Path(), config)
await dispatcher.fire(config_loaded_event)


async def fire_arg_parse_event(dispatcher: Dispatcher) -> None:
arg_parse_event = ArgParseEvent(ArgumentParser())
await dispatcher.fire(arg_parse_event)


def make_arg_parsed_event(*, order_stable: bool = False,
order_reversed: bool = False,
order_random: bool = False) -> ArgParsedEvent:
return ArgParsedEvent(
Namespace(order_stable=order_stable,
order_reversed=order_reversed,
order_random=order_random))
80 changes: 80 additions & 0 deletions tests/plugins/orderer/test_orderer_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from unittest.mock import Mock, call

import pytest
from baby_steps import given, then, when

from vedro import Config
from vedro.core import Dispatcher
from vedro.plugins.orderer import (
OrdererPlugin,
RandomOrderer,
ReversedOrderer,
StableScenarioOrderer,
)

from ._utils import (
dispatcher,
fire_arg_parse_event,
fire_config_loaded_event,
make_arg_parsed_event,
orderer,
)

__all__ = ("orderer", "dispatcher") # fixtures


@pytest.mark.asyncio
async def test_stable_orderer(*, orderer: OrdererPlugin, dispatcher: Dispatcher):
with given:
config_ = Mock(Config)

await fire_config_loaded_event(dispatcher, config_)
await fire_arg_parse_event(dispatcher)

event = make_arg_parsed_event(order_stable=True)

with when:
await dispatcher.fire(event)

with then:
assert config_.mock_calls == [
call.Registry.ScenarioOrderer.register(StableScenarioOrderer, orderer)
]


@pytest.mark.asyncio
async def test_reversed_orderer(*, orderer: OrdererPlugin, dispatcher: Dispatcher):
with given:
config_ = Mock(Config)

await fire_config_loaded_event(dispatcher, config_)
await fire_arg_parse_event(dispatcher)

event = make_arg_parsed_event(order_reversed=True)

with when:
await dispatcher.fire(event)

with then:
assert config_.mock_calls == [
call.Registry.ScenarioOrderer.register(ReversedOrderer, orderer)
]


@pytest.mark.asyncio
async def test_random_orderer(*, orderer: OrdererPlugin, dispatcher: Dispatcher):
with given:
config_ = Mock(Config)

await fire_config_loaded_event(dispatcher, config_)
await fire_arg_parse_event(dispatcher)

event = make_arg_parsed_event(order_random=True)

with when:
await dispatcher.fire(event)

with then:
assert config_.mock_calls == [
call.Registry.ScenarioOrderer.register(RandomOrderer, orderer)
]
52 changes: 52 additions & 0 deletions tests/plugins/orderer/test_random_scenario_orderer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import pytest
from baby_steps import given, then, when

from vedro.plugins.orderer import RandomOrderer

from ._utils import make_vscenario, seeded


@pytest.fixture()
def orderer() -> RandomOrderer:
return RandomOrderer()


@pytest.mark.asyncio
async def test_sort_no_scenarios(*, orderer: RandomOrderer):
with given:
scenarios = []

with when:
res = await orderer.sort(scenarios)

with then:
assert res == []
assert scenarios == []


@pytest.mark.asyncio
async def test_sort_scenarios(*, orderer: RandomOrderer):
with given:
orig_scenarios = {path: make_vscenario(path) for path in [
"scenarios/directory/scn.py",
"scenarios/dir1/scn1.py",
"scenarios/dir1/scn2.py",
"scenarios/dir2/scn2.py",
"scenarios/scn2.py",
"scenarios/scn10.py",
]}
scenarios = list(orig_scenarios.values())

with when, seeded("c104234e-c400-4648-aa3f-5442ce5ed1fc"):
res = await orderer.sort(scenarios)

with then:
assert res == [
orig_scenarios["scenarios/directory/scn.py"],
orig_scenarios["scenarios/dir1/scn2.py"],
orig_scenarios["scenarios/scn10.py"],
orig_scenarios["scenarios/scn2.py"],
orig_scenarios["scenarios/dir1/scn1.py"],
orig_scenarios["scenarios/dir2/scn2.py"],
]
assert scenarios == list(orig_scenarios.values())
52 changes: 52 additions & 0 deletions tests/plugins/orderer/test_reversed_scenario_orderer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import pytest
from baby_steps import given, then, when

from vedro.plugins.orderer import ReversedOrderer

from ._utils import make_vscenario


@pytest.fixture()
def orderer() -> ReversedOrderer:
return ReversedOrderer()


@pytest.mark.asyncio
async def test_sort_no_scenarios(*, orderer: ReversedOrderer):
with given:
scenarios = []

with when:
res = await orderer.sort(scenarios)

with then:
assert res == []
assert scenarios == []


@pytest.mark.asyncio
async def test_sort_scenarios(*, orderer: ReversedOrderer):
with given:
orig_scenarios = {path: make_vscenario(path) for path in [
"scenarios/directory/scn.py",
"scenarios/dir1/scn1.py",
"scenarios/dir1/scn2.py",
"scenarios/dir2/scn2.py",
"scenarios/scn2.py",
"scenarios/scn10.py",
]}
scenarios = list(orig_scenarios.values())

with when:
res = await orderer.sort(scenarios)

with then:
assert res == [
orig_scenarios["scenarios/directory/scn.py"],
orig_scenarios["scenarios/dir2/scn2.py"],
orig_scenarios["scenarios/dir1/scn2.py"],
orig_scenarios["scenarios/dir1/scn1.py"],
orig_scenarios["scenarios/scn10.py"],
orig_scenarios["scenarios/scn2.py"],
]
assert scenarios == list(orig_scenarios.values())
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
from pathlib import Path

import pytest
from baby_steps import given, then, when

from vedro import Scenario
from vedro.core import StableScenarioOrderer, VirtualScenario
from vedro.plugins.orderer import StableScenarioOrderer

from ._utils import make_vscenario


@pytest.fixture()
def orderer():
def orderer() -> StableScenarioOrderer:
return StableScenarioOrderer()


def make_vscenario(path: str) -> VirtualScenario:
class _Scenario(Scenario):
__file__ = Path(path).absolute()

return VirtualScenario(_Scenario, steps=[])


@pytest.mark.asyncio
async def test_sort_no_scenarios(*, orderer: StableScenarioOrderer):
with given:
Expand Down
4 changes: 2 additions & 2 deletions vedro/_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@
ScenarioRunner,
ScenarioScheduler,
Singleton,
StableScenarioOrderer,
)
from vedro.core._scenario_finder._file_filters import (
AnyFilter,
DunderFilter,
ExtFilter,
HiddenFilter,
)
from vedro.core._scenario_orderer import PlainScenarioOrderer

__all__ = ("Config",)

Expand All @@ -52,7 +52,7 @@ class Registry(core.Config.Registry):

ScenarioLoader = Factory[ScenarioLoader](ScenarioFileLoader)

ScenarioOrderer = Factory[ScenarioOrderer](StableScenarioOrderer)
ScenarioOrderer = Factory[ScenarioOrderer](PlainScenarioOrderer)

ScenarioDiscoverer = Factory[ScenarioDiscoverer](lambda: MultiScenarioDiscoverer(
finder=Config.Registry.ScenarioFinder(),
Expand Down
4 changes: 2 additions & 2 deletions vedro/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from ._scenario_discoverer import MultiScenarioDiscoverer, ScenarioDiscoverer
from ._scenario_finder import ScenarioFileFinder, ScenarioFinder
from ._scenario_loader import ScenarioFileLoader, ScenarioLoader
from ._scenario_orderer import ScenarioOrderer, StableScenarioOrderer
from ._scenario_orderer import ScenarioOrderer
from ._scenario_result import AggregatedResult, ScenarioResult, ScenarioStatus
from ._scenario_runner import MonotonicScenarioRunner, ScenarioRunner
from ._scenario_scheduler import MonotonicScenarioScheduler, ScenarioScheduler
Expand All @@ -28,4 +28,4 @@
"ConfigLoader", "ConfigFileLoader", "Config", "Section", "ConfigType",
"ModuleLoader", "ModuleFileLoader", "Artifact", "MemoryArtifact", "FileArtifact",
"ScenarioScheduler", "MonotonicScenarioScheduler", "FactoryType",
"Container", "Factory", "Singleton", "ScenarioOrderer", "StableScenarioOrderer")
"Container", "Factory", "Singleton", "ScenarioOrderer")
4 changes: 2 additions & 2 deletions vedro/core/_scenario_orderer/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from ._plain_scenario_orderer import PlainScenarioOrderer
from ._scenario_orderer import ScenarioOrderer
from ._stable_scenario_orderer import StableScenarioOrderer

__all__ = ("ScenarioOrderer", "StableScenarioOrderer",)
__all__ = ("ScenarioOrderer", "PlainScenarioOrderer",)
11 changes: 11 additions & 0 deletions vedro/core/_scenario_orderer/_plain_scenario_orderer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from typing import List

from .._virtual_scenario import VirtualScenario
from ._scenario_orderer import ScenarioOrderer

__all__ = ("PlainScenarioOrderer",)


class PlainScenarioOrderer(ScenarioOrderer):
async def sort(self, scenarios: List[VirtualScenario]) -> List[VirtualScenario]:
return scenarios
7 changes: 7 additions & 0 deletions vedro/plugins/orderer/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from .orderer_plugin import Orderer, OrdererPlugin
from .random_orderer import RandomOrderer
from .reversed_orderer import ReversedOrderer
from .stable_orderer import StableScenarioOrderer

__all__ = ("Orderer", "OrdererPlugin",
"StableScenarioOrderer", "ReversedOrderer", "RandomOrderer",)
43 changes: 43 additions & 0 deletions vedro/plugins/orderer/orderer_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from vedro.core import ConfigType, Dispatcher, Plugin, PluginConfig
from vedro.events import ArgParsedEvent, ArgParseEvent, ConfigLoadedEvent

from .random_orderer import RandomOrderer
from .reversed_orderer import ReversedOrderer
from .stable_orderer import StableScenarioOrderer

__all__ = ("Orderer", "OrdererPlugin")


class OrdererPlugin(Plugin):
def subscribe(self, dispatcher: Dispatcher) -> None:
dispatcher.listen(ConfigLoadedEvent, self.on_config_loaded) \
.listen(ArgParseEvent, self.on_arg_parse) \
.listen(ArgParsedEvent, self.on_arg_parsed)

def on_config_loaded(self, event: ConfigLoadedEvent) -> None:
self._global_config: ConfigType = event.config

def on_arg_parse(self, event: ArgParseEvent) -> None:
group = event.arg_parser.add_argument_group("Orderer")
exgroup = group.add_mutually_exclusive_group()

exgroup.add_argument("--order-stable", action="store_true", default=True,
help="Set stable scenario order (default)")
exgroup.add_argument("--order-reversed", action="store_true", default=False,
help="Set reversed scenario order")
exgroup.add_argument("--order-random", action="store_true", default=False,
help="Set random scenario order")

def on_arg_parsed(self, event: ArgParsedEvent) -> None:
if event.args.order_stable:
self._global_config.Registry.ScenarioOrderer.register(StableScenarioOrderer, self)

if event.args.order_reversed:
self._global_config.Registry.ScenarioOrderer.register(ReversedOrderer, self)

if event.args.order_random:
self._global_config.Registry.ScenarioOrderer.register(RandomOrderer, self)


class Orderer(PluginConfig):
plugin = OrdererPlugin
13 changes: 13 additions & 0 deletions vedro/plugins/orderer/random_orderer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import random
from typing import List

from vedro.core import ScenarioOrderer, VirtualScenario

__all__ = ("RandomOrderer",)


class RandomOrderer(ScenarioOrderer):
async def sort(self, scenarios: List[VirtualScenario]) -> List[VirtualScenario]:
copied = scenarios[:]
random.shuffle(copied)
return copied
Loading