test SafeEyesCore: refactor out run_next_break method
This commit is contained in:
parent
affd1af5e0
commit
d43e8dc8bb
|
@ -14,7 +14,7 @@ from unittest import mock
|
||||||
class TestSafeEyesCore:
|
class TestSafeEyesCore:
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def set_time(self, time_machine):
|
def set_time(self, time_machine):
|
||||||
time_machine.move_to(datetime.datetime.fromisoformat("2024-08-25T13:00"))
|
time_machine.move_to(datetime.datetime.fromisoformat("2024-08-25T13:00:00+00:00"), tick=False)
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def monkeypatch_translations(self, monkeypatch):
|
def monkeypatch_translations(self, monkeypatch):
|
||||||
|
@ -22,7 +22,7 @@ class TestSafeEyesCore:
|
||||||
monkeypatch.setattr(model, "_", lambda message: "translated!: " + message, raising=False)
|
monkeypatch.setattr(model, "_", lambda message: "translated!: " + message, raising=False)
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def sequential_threading(self, monkeypatch):
|
def sequential_threading(self, monkeypatch, time_machine):
|
||||||
# executes instantly
|
# executes instantly
|
||||||
# TODO: separate thread?
|
# TODO: separate thread?
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
|
@ -39,6 +39,9 @@ class TestSafeEyesCore:
|
||||||
condvar_in = threading.Condition()
|
condvar_in = threading.Condition()
|
||||||
condvar_out = threading.Condition()
|
condvar_out = threading.Condition()
|
||||||
|
|
||||||
|
def __init__(self, time_machine):
|
||||||
|
self.time_machine = time_machine
|
||||||
|
|
||||||
def background_thread(self):
|
def background_thread(self):
|
||||||
while True:
|
while True:
|
||||||
with self.condvar_in:
|
with self.condvar_in:
|
||||||
|
@ -65,6 +68,7 @@ class TestSafeEyesCore:
|
||||||
if self.thread is threading.current_thread():
|
if self.thread is threading.current_thread():
|
||||||
with self.condvar_out:
|
with self.condvar_out:
|
||||||
self.condvar_out.notify()
|
self.condvar_out.notify()
|
||||||
|
self.time_machine.shift(delta=datetime.timedelta(seconds=time))
|
||||||
with self.condvar_in:
|
with self.condvar_in:
|
||||||
success = self.condvar_in.wait(1)
|
success = self.condvar_in.wait(1)
|
||||||
if not success:
|
if not success:
|
||||||
|
@ -98,7 +102,7 @@ class TestSafeEyesCore:
|
||||||
if self.thread:
|
if self.thread:
|
||||||
self.thread.join(1)
|
self.thread.join(1)
|
||||||
|
|
||||||
handle = Handle()
|
handle = Handle(time_machine=time_machine)
|
||||||
|
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
core.utility,
|
core.utility,
|
||||||
|
@ -190,7 +194,7 @@ class TestSafeEyesCore:
|
||||||
|
|
||||||
# start __scheduler_job
|
# start __scheduler_job
|
||||||
sequential_threading.next()
|
sequential_threading.next()
|
||||||
# FIXME: sleep is needed so code reaches the Condition
|
# FIXME: sleep is needed so code reaches the waiting_condition
|
||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
assert context['state'] == model.State.WAITING
|
assert context['state'] == model.State.WAITING
|
||||||
|
|
||||||
|
@ -216,60 +220,48 @@ class TestSafeEyesCore:
|
||||||
|
|
||||||
logging.debug("done")
|
logging.debug("done")
|
||||||
|
|
||||||
|
def run_next_break(
|
||||||
|
self,
|
||||||
|
sequential_threading,
|
||||||
|
time_machine,
|
||||||
|
safe_eyes_core,
|
||||||
|
context,
|
||||||
|
break_duration,
|
||||||
|
break_interval,
|
||||||
|
pre_break_warning_time,
|
||||||
|
break_name_translated
|
||||||
|
):
|
||||||
|
"""Run one entire cycle of safe_eyes_core.
|
||||||
|
|
||||||
|
It must be waiting for __scheduler_job to run. (This is the equivalent of State.WAITING).
|
||||||
|
That means it must either be just started, or have finished the previous cycle.
|
||||||
|
"""
|
||||||
|
|
||||||
def test_actual(self, sequential_threading, time_machine):
|
|
||||||
context = {
|
|
||||||
"session": {},
|
|
||||||
}
|
|
||||||
config = {
|
|
||||||
"short_breaks": [
|
|
||||||
{"name": "break 1"},
|
|
||||||
{"name": "break 2"},
|
|
||||||
{"name": "break 3"},
|
|
||||||
{"name": "break 4"},
|
|
||||||
],
|
|
||||||
"long_breaks": [
|
|
||||||
{"name": "long break 1"},
|
|
||||||
{"name": "long break 2"},
|
|
||||||
{"name": "long break 3"},
|
|
||||||
],
|
|
||||||
"short_break_interval": 15,
|
|
||||||
"long_break_interval": 75,
|
|
||||||
"long_break_duration": 60,
|
|
||||||
"short_break_duration": 15,
|
|
||||||
"pre_break_warning_time": 10,
|
|
||||||
"random_order": False,
|
|
||||||
"postpone_duration": 5,
|
|
||||||
}
|
|
||||||
on_update_next_break = mock.Mock()
|
on_update_next_break = mock.Mock()
|
||||||
on_pre_break = mock.Mock(return_value=True)
|
on_pre_break = mock.Mock(return_value=True)
|
||||||
on_start_break = mock.Mock(return_value=True)
|
on_start_break = mock.Mock(return_value=True)
|
||||||
start_break = mock.Mock()
|
start_break = mock.Mock()
|
||||||
on_count_down = mock.Mock()
|
on_count_down = mock.Mock()
|
||||||
safe_eyes_core = core.SafeEyesCore(context)
|
|
||||||
safe_eyes_core.on_update_next_break += on_update_next_break
|
safe_eyes_core.on_update_next_break += on_update_next_break
|
||||||
safe_eyes_core.on_pre_break += on_pre_break
|
safe_eyes_core.on_pre_break += on_pre_break
|
||||||
safe_eyes_core.on_start_break += on_start_break
|
safe_eyes_core.on_start_break += on_start_break
|
||||||
safe_eyes_core.start_break += start_break
|
safe_eyes_core.start_break += start_break
|
||||||
safe_eyes_core.on_count_down += on_count_down
|
safe_eyes_core.on_count_down += on_count_down
|
||||||
|
|
||||||
safe_eyes_core.initialize(config)
|
|
||||||
|
|
||||||
safe_eyes_core.start()
|
|
||||||
|
|
||||||
# start __scheduler_job
|
# start __scheduler_job
|
||||||
sequential_threading.next()
|
sequential_threading.next()
|
||||||
# FIXME: sleep is needed so code reaches the Condition
|
# FIXME: sleep is needed so code reaches the waiting_condition
|
||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
assert context['state'] == model.State.WAITING
|
assert context['state'] == model.State.WAITING
|
||||||
|
|
||||||
on_update_next_break.assert_called_once()
|
on_update_next_break.assert_called_once()
|
||||||
assert isinstance(on_update_next_break.call_args[0][0], model.Break)
|
assert isinstance(on_update_next_break.call_args[0][0], model.Break)
|
||||||
assert on_update_next_break.call_args[0][0].name == "translated!: break 1"
|
assert on_update_next_break.call_args[0][0].name == break_name_translated
|
||||||
on_update_next_break.reset_mock()
|
on_update_next_break.reset_mock()
|
||||||
|
|
||||||
with safe_eyes_core.lock:
|
with safe_eyes_core.lock:
|
||||||
time_machine.shift(delta=datetime.timedelta(minutes=15))
|
time_machine.shift(delta=datetime.timedelta(minutes=break_interval))
|
||||||
|
|
||||||
with safe_eyes_core.waiting_condition:
|
with safe_eyes_core.waiting_condition:
|
||||||
logging.debug("notify")
|
logging.debug("notify")
|
||||||
|
@ -283,16 +275,16 @@ class TestSafeEyesCore:
|
||||||
|
|
||||||
on_pre_break.assert_called_once()
|
on_pre_break.assert_called_once()
|
||||||
assert isinstance(on_pre_break.call_args[0][0], model.Break)
|
assert isinstance(on_pre_break.call_args[0][0], model.Break)
|
||||||
assert on_pre_break.call_args[0][0].name == "translated!: break 1"
|
assert on_pre_break.call_args[0][0].name == break_name_translated
|
||||||
on_pre_break.reset_mock()
|
on_pre_break.reset_mock()
|
||||||
|
|
||||||
# start __wait_until_prepare
|
# start __wait_until_prepare
|
||||||
sequential_threading.next()
|
sequential_threading.next()
|
||||||
|
|
||||||
# FIXME: sleep is needed so code reaches the Condition
|
# FIXME: sleep is needed so code reaches the waiting_condition
|
||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
with safe_eyes_core.lock:
|
with safe_eyes_core.lock:
|
||||||
time_machine.shift(delta=datetime.timedelta(seconds=10))
|
time_machine.shift(delta=datetime.timedelta(seconds=pre_break_warning_time))
|
||||||
|
|
||||||
with safe_eyes_core.waiting_condition:
|
with safe_eyes_core.waiting_condition:
|
||||||
logging.debug("notify")
|
logging.debug("notify")
|
||||||
|
@ -311,18 +303,18 @@ class TestSafeEyesCore:
|
||||||
|
|
||||||
on_start_break.assert_called_once()
|
on_start_break.assert_called_once()
|
||||||
assert isinstance(on_start_break.call_args[0][0], model.Break)
|
assert isinstance(on_start_break.call_args[0][0], model.Break)
|
||||||
assert on_start_break.call_args[0][0].name == "translated!: break 1"
|
assert on_start_break.call_args[0][0].name == break_name_translated
|
||||||
on_start_break.reset_mock()
|
on_start_break.reset_mock()
|
||||||
|
|
||||||
start_break.assert_called_once()
|
start_break.assert_called_once()
|
||||||
assert isinstance(start_break.call_args[0][0], model.Break)
|
assert isinstance(start_break.call_args[0][0], model.Break)
|
||||||
assert start_break.call_args[0][0].name == "translated!: break 1"
|
assert start_break.call_args[0][0].name == break_name_translated
|
||||||
start_break.reset_mock()
|
start_break.reset_mock()
|
||||||
|
|
||||||
assert context['state'] == model.State.BREAK
|
assert context['state'] == model.State.BREAK
|
||||||
|
|
||||||
# continue sleep in __start_break
|
# continue sleep in __start_break
|
||||||
for i in range(config["short_break_duration"] - 1):
|
for i in range(break_duration - 1):
|
||||||
sequential_threading.wait()
|
sequential_threading.wait()
|
||||||
sequential_threading.next()
|
sequential_threading.next()
|
||||||
|
|
||||||
|
@ -331,15 +323,131 @@ class TestSafeEyesCore:
|
||||||
logging.debug("done waiting for end of __start_break")
|
logging.debug("done waiting for end of __start_break")
|
||||||
|
|
||||||
on_count_down.assert_called()
|
on_count_down.assert_called()
|
||||||
assert on_count_down.call_count == 15
|
assert on_count_down.call_count == break_duration
|
||||||
on_count_down.reset_mock()
|
on_count_down.reset_mock()
|
||||||
|
|
||||||
assert context['state'] == model.State.BREAK
|
assert context['state'] == model.State.BREAK
|
||||||
|
|
||||||
|
def assert_datetime(self, string):
|
||||||
|
if not string.endswith("+00:00"):
|
||||||
|
string += "+00:00"
|
||||||
|
assert datetime.datetime.now(datetime.timezone.utc) == datetime.datetime.fromisoformat(string)
|
||||||
|
|
||||||
|
def test_actual(self, sequential_threading, time_machine):
|
||||||
|
context = {
|
||||||
|
"session": {},
|
||||||
|
}
|
||||||
|
short_break_duration = 15 # seconds
|
||||||
|
short_break_interval = 15 # minutes
|
||||||
|
pre_break_warning_time = 10 # seconds
|
||||||
|
long_break_duration = 60 # seconds
|
||||||
|
long_break_interval = 75 # minutes
|
||||||
|
config = {
|
||||||
|
"short_breaks": [
|
||||||
|
{"name": "break 1"},
|
||||||
|
{"name": "break 2"},
|
||||||
|
{"name": "break 3"},
|
||||||
|
{"name": "break 4"},
|
||||||
|
],
|
||||||
|
"long_breaks": [
|
||||||
|
{"name": "long break 1"},
|
||||||
|
{"name": "long break 2"},
|
||||||
|
{"name": "long break 3"},
|
||||||
|
],
|
||||||
|
"short_break_interval": short_break_interval,
|
||||||
|
"long_break_interval": long_break_interval,
|
||||||
|
"long_break_duration": long_break_duration,
|
||||||
|
"short_break_duration": short_break_duration,
|
||||||
|
"pre_break_warning_time": pre_break_warning_time,
|
||||||
|
"random_order": False,
|
||||||
|
"postpone_duration": 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
self.assert_datetime("2024-08-25T13:00:00")
|
||||||
|
|
||||||
|
safe_eyes_core = core.SafeEyesCore(context)
|
||||||
|
|
||||||
|
safe_eyes_core.initialize(config)
|
||||||
|
|
||||||
|
safe_eyes_core.start()
|
||||||
|
|
||||||
|
|
||||||
|
self.run_next_break(
|
||||||
|
sequential_threading,
|
||||||
|
time_machine,
|
||||||
|
safe_eyes_core,
|
||||||
|
context,
|
||||||
|
short_break_duration,
|
||||||
|
short_break_interval,
|
||||||
|
pre_break_warning_time,
|
||||||
|
"translated!: break 1"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assert_datetime("2024-08-25T13:15:25")
|
||||||
|
|
||||||
|
self.run_next_break(
|
||||||
|
sequential_threading,
|
||||||
|
time_machine,
|
||||||
|
safe_eyes_core,
|
||||||
|
context,
|
||||||
|
short_break_duration,
|
||||||
|
short_break_interval,
|
||||||
|
pre_break_warning_time,
|
||||||
|
"translated!: break 2"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assert_datetime("2024-08-25T13:30:50")
|
||||||
|
|
||||||
|
self.run_next_break(
|
||||||
|
sequential_threading,
|
||||||
|
time_machine,
|
||||||
|
safe_eyes_core,
|
||||||
|
context,
|
||||||
|
short_break_duration,
|
||||||
|
short_break_interval,
|
||||||
|
pre_break_warning_time,
|
||||||
|
"translated!: break 3"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assert_datetime("2024-08-25T13:46:15")
|
||||||
|
|
||||||
|
self.run_next_break(
|
||||||
|
sequential_threading,
|
||||||
|
time_machine,
|
||||||
|
safe_eyes_core,
|
||||||
|
context,
|
||||||
|
short_break_duration,
|
||||||
|
short_break_interval,
|
||||||
|
pre_break_warning_time,
|
||||||
|
"translated!: break 4"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assert_datetime("2024-08-25T14:01:40")
|
||||||
|
|
||||||
|
self.run_next_break(
|
||||||
|
sequential_threading,
|
||||||
|
time_machine,
|
||||||
|
safe_eyes_core,
|
||||||
|
context,
|
||||||
|
long_break_duration,
|
||||||
|
long_break_interval,
|
||||||
|
pre_break_warning_time,
|
||||||
|
"translated!: long break 1"
|
||||||
|
)
|
||||||
|
|
||||||
|
#self.assert_datetime("2024-08-25T14:16:40")
|
||||||
|
|
||||||
|
self.run_next_break(
|
||||||
|
sequential_threading,
|
||||||
|
time_machine,
|
||||||
|
safe_eyes_core,
|
||||||
|
context,
|
||||||
|
short_break_duration,
|
||||||
|
short_break_interval,
|
||||||
|
pre_break_warning_time,
|
||||||
|
"translated!: break 1"
|
||||||
|
)
|
||||||
|
|
||||||
safe_eyes_core.stop()
|
safe_eyes_core.stop()
|
||||||
|
|
||||||
on_update_next_break.assert_not_called()
|
|
||||||
on_pre_break.assert_not_called()
|
|
||||||
on_start_break.assert_not_called()
|
|
||||||
start_break.assert_not_called()
|
|
||||||
assert context['state'] == model.State.STOPPED
|
assert context['state'] == model.State.STOPPED
|
||||||
|
|
Loading…
Reference in New Issue