Skip to content

Commit

Permalink
add more tests for coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
bckohan committed Jun 4, 2024
1 parent 6b5e4e5 commit 583c152
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 38 deletions.
51 changes: 34 additions & 17 deletions django_routines/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@

import bisect
import sys
import typing as t
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, cast

VERSION = (1, 0, 0)

Expand All @@ -24,17 +24,29 @@
__license__ = "MIT"
__copyright__ = "Copyright 2024 Brian Kohan"

__all__ = [
"ROUTINE_SETTING",
"RoutineCommand",
"Routine",
"routine",
"command",
"get_routine",
]


ROUTINE_SETTING = "ROUTINES"


R = t.TypeVar("R")


@dataclass
class RoutineCommand:
"""
Dataclass to hold the routine command information.
"""

command: Tuple[str, ...]
command: t.Tuple[str, ...]
"""
The command and its arguments to run the routine, all strings or
coercible to strings that the command will parse correctly.
Expand All @@ -46,20 +58,25 @@ class RoutineCommand:
insertion order.
"""

switches: Tuple[str, ...] = tuple()
switches: t.Tuple[str, ...] = tuple()
"""
The command will run only when one of these switches is active,
or for all invocations of the routine if no switches are configured.
"""

options: Dict[str, Any] = field(default_factory=dict)
options: t.Dict[str, t.Any] = field(default_factory=dict)
"""
Any options to pass to the command via call_command.
t.Any options to pass to the command via call_command.
"""


# todo - remove this and replace with key argument when support for python <3.10 dropped.
def insort_right_with_key(a, x, key=lambda x: x):
def _insort_right_with_key(a: t.List[R], x: R, key: t.Callable[[R], t.Any]) -> None:
"""
A function that implements bisect.insort_right with a key callable on items.
todo: remove this and replace with key argument to bisect.insort_right when support for
python <3.10 dropped.
"""
transformed_list = [key(item) for item in a]
transformed_x = key(x)
insert_point = bisect.bisect_right(transformed_list, transformed_x)
Expand All @@ -82,25 +99,25 @@ class Routine:
The help text to display for the routine.
"""

commands: List[RoutineCommand] = field(default_factory=list)
commands: t.List[RoutineCommand] = field(default_factory=list)
"""
The commands to run in the routine.
"""

switch_helps: Dict[str, str] = field(default_factory=dict)
switch_helps: t.Dict[str, str] = field(default_factory=dict)

def __len__(self):
return len(self.commands)

@property
def switches(self) -> Set[str]:
switches: Set[str] = set()
def switches(self) -> t.Set[str]:
switches: t.Set[str] = set()
for command in self.commands:
if command.switches:
switches.update(command.switches)
return switches

def plan(self, switches: Set[str]) -> List[RoutineCommand]:
def plan(self, switches: t.Set[str]) -> t.List[RoutineCommand]:
return [
command
for command in self.commands
Expand All @@ -111,7 +128,7 @@ def plan(self, switches: Set[str]) -> List[RoutineCommand]:
def command(self, command: RoutineCommand):
# python >= 3.10
# bisect.insort(self.commands, command, key=lambda cmd: cmd.priority)
insort_right_with_key(self.commands, command, key=lambda cmd: cmd.priority)
_insort_right_with_key(self.commands, command, key=lambda cmd: cmd.priority)
return command


Expand All @@ -137,7 +154,7 @@ def get_routine(name: str) -> Routine:

def routine(name: str, help_text: str = "", *commands: RoutineCommand, **switch_helps):
"""
Register a routine to the list of routines in settings to be run.
Register a routine to the t.List of routines in settings to be run.
:param name: The name of the routine to register.
:param help_text: The help text to display for the routine by the routines command.
Expand All @@ -155,7 +172,7 @@ def command(
routine: str,
*command: str,
priority: int = RoutineCommand.priority,
switches: Optional[Sequence[str]] = RoutineCommand.switches,
switches: t.Optional[t.Sequence[str]] = RoutineCommand.switches,
**options,
):
"""
Expand All @@ -172,14 +189,14 @@ def command(
insertion order.
:param switches: The command will run only when one of these switches is active,
or for all invocations of the routine if no switches are configured.
:param options: Any options to pass to the command via call_command.
:param options: t.Any options to pass to the command via call_command.
:return: The new command.
"""
settings = sys._getframe(1).f_globals # noqa: WPS437
settings.setdefault(ROUTINE_SETTING, {})
settings[ROUTINE_SETTING].setdefault(routine, Routine(routine, "", []))
return settings[ROUTINE_SETTING][routine].command(
RoutineCommand(
cast(Tuple[str], command), priority, tuple(switches or []), options
t.cast(t.Tuple[str], command), priority, tuple(switches or []), options
)
)
8 changes: 8 additions & 0 deletions tests/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,11 @@
command("test", "track", "3", priority=3, demo=2)
command("test", "track", "4", priority=3, demo=6)
command("test", "track", "5", priority=6, switches=["demo"])

routine(
"bad",
_("Bad command test routine"),
RoutineCommand(("track", "0")),
RoutineCommand(("does_not_exist",)),
RoutineCommand(("track", "1")),
)
64 changes: 43 additions & 21 deletions tests/tests.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from io import StringIO

from django.core.management import call_command
from django.core.management import call_command, CommandError
from django.test import TestCase
import re

Expand Down Expand Up @@ -35,10 +35,13 @@ def test_command(self, no_color=True, verbosity=None):
out = StringIO()
call_command(*command, stdout=out)
expected = [3, 4, 1]
self.assertEqual(
self.lines(out.getvalue(), no_color=no_color),
[f"track {id}" for id in expected],
)
if verbosity is None or verbosity > 0:
self.assertEqual(
self.lines(out.getvalue(), no_color=no_color),
[f"track {id}" for id in expected],
)
else:
self.assertFalse(out.getvalue().strip())
self.assertEqual(invoked, expected)
self.assertEqual(passed_options[0]["demo"], 2)
self.assertEqual(passed_options[1]["demo"], 6)
Expand All @@ -51,10 +54,13 @@ def test_command(self, no_color=True, verbosity=None):
out = StringIO()
call_command(*command, "--all", stdout=out)
expected = [2, 0, 3, 4, 1, 5]
self.assertEqual(
self.lines(out.getvalue(), no_color=no_color),
[f"track {id}" for id in expected],
)
if verbosity is None or verbosity > 0:
self.assertEqual(
self.lines(out.getvalue(), no_color=no_color),
[f"track {id}" for id in expected],
)
else:
self.assertFalse(out.getvalue().strip())
self.assertEqual(invoked, expected)
self.assertEqual(passed_options[1]["verbosity"], 0)
self.assertEqual(passed_options[2]["demo"], 2)
Expand All @@ -69,10 +75,13 @@ def test_command(self, no_color=True, verbosity=None):
out = StringIO()
call_command(*command, "--demo", stdout=out)
expected = [2, 3, 4, 1, 5]
self.assertEqual(
self.lines(out.getvalue(), no_color=no_color),
[f"track {id}" for id in expected],
)
if verbosity is None or verbosity > 0:
self.assertEqual(
self.lines(out.getvalue(), no_color=no_color),
[f"track {id}" for id in expected],
)
else:
self.assertFalse(out.getvalue().strip())
self.assertEqual(invoked, expected)
self.assertEqual(passed_options[1]["demo"], 2)
self.assertEqual(passed_options[2]["demo"], 6)
Expand All @@ -85,10 +94,13 @@ def test_command(self, no_color=True, verbosity=None):
out = StringIO()
call_command(*command, "--demo", "--initial", stdout=out)
expected = [2, 0, 3, 4, 1, 5]
self.assertEqual(
self.lines(out.getvalue(), no_color=no_color),
[f"track {id}" for id in expected],
)
if verbosity is None or verbosity > 0:
self.assertEqual(
self.lines(out.getvalue(), no_color=no_color),
[f"track {id}" for id in expected],
)
else:
self.assertFalse(out.getvalue().strip())
self.assertEqual(invoked, expected)
self.assertEqual(passed_options[1]["verbosity"], 0)
self.assertEqual(passed_options[2]["demo"], 2)
Expand All @@ -103,10 +115,13 @@ def test_command(self, no_color=True, verbosity=None):
out = StringIO()
call_command(*command, "--initial", stdout=out)
expected = [2, 0, 3, 4, 1]
self.assertEqual(
self.lines(out.getvalue(), no_color=no_color),
[f"track {id}" for id in expected],
)
if verbosity is None or verbosity > 0:
self.assertEqual(
self.lines(out.getvalue(), no_color=no_color),
[f"track {id}" for id in expected],
)
else:
self.assertFalse(out.getvalue().strip())
self.assertEqual(invoked, expected)
self.assertEqual(passed_options[1]["verbosity"], 0)
self.assertEqual(passed_options[2]["demo"], 2)
Expand All @@ -123,6 +138,9 @@ def test_command_color(self):

def test_verbosity(self):
self.test_command(verbosity=3)
self.test_command(verbosity=0)
self.test_command(verbosity=2)
self.test_command(verbosity=1)

def test_list(self, no_color=True):
if no_color:
Expand Down Expand Up @@ -197,3 +215,7 @@ def test_list(self, no_color=True):

def test_list_color(self):
self.test_list(no_color=False)

def test_routine_with_bad_command(self):
with self.assertRaises(CommandError):
call_command("routine", "bad")

0 comments on commit 583c152

Please sign in to comment.