390 lines
14 KiB
Python
390 lines
14 KiB
Python
|
from datetime import datetime
|
||
|
from unittest.mock import patch
|
||
|
|
||
|
import pytest
|
||
|
|
||
|
from parental_control import DATETIME_FORMAT, app
|
||
|
from parental_control.span import DateTimeSpan, MinuteSpan
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize(
|
||
|
"point_in_time, schedules, overrides, expected_result",
|
||
|
[
|
||
|
# Point in time within override
|
||
|
(
|
||
|
"2023-08-24 14:00",
|
||
|
{day: [] for day in range(7)},
|
||
|
[{"start": "2023-08-24 12:00", "end": "2023-08-24 18:00", "action": True}],
|
||
|
True,
|
||
|
),
|
||
|
# Point in time within schedule
|
||
|
(
|
||
|
"2023-08-24 14:00",
|
||
|
{3: [{"start": "10:00", "end": "16:00"}]},
|
||
|
[],
|
||
|
True,
|
||
|
),
|
||
|
# Point in time outside schedule and override
|
||
|
(
|
||
|
"2023-08-24 08:00",
|
||
|
{3: [{"start": "10:00", "end": "16:00"}]},
|
||
|
[{"start": "2023-08-24 12:00", "end": "2023-08-24 18:00", "action": True}],
|
||
|
False,
|
||
|
),
|
||
|
],
|
||
|
)
|
||
|
def test_get_action_at(point_in_time, schedules, overrides, expected_result):
|
||
|
point_in_time = datetime.strptime(point_in_time, DATETIME_FORMAT)
|
||
|
schedules = {
|
||
|
day: [MinuteSpan.from_args(**span) for span in schedule]
|
||
|
for day, schedule in schedules.items()
|
||
|
}
|
||
|
overrides = [DateTimeSpan.from_args(**o) for o in overrides]
|
||
|
|
||
|
result = app.get_action_at(point_in_time, schedules, overrides)
|
||
|
|
||
|
assert result == expected_result
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize(
|
||
|
"point_in_time, action, schedules, overrides, expected_result",
|
||
|
[
|
||
|
# No schedule, no overrides, next change will never occur
|
||
|
(
|
||
|
"2023-08-24 08:00",
|
||
|
False,
|
||
|
{day: [] for day in range(7)},
|
||
|
[],
|
||
|
None,
|
||
|
),
|
||
|
# Schedule 24/7, no overrides, next change will never occur
|
||
|
(
|
||
|
"2023-08-24 08:00",
|
||
|
True,
|
||
|
{day: [{"start": "00:00", "end": "24:00"}] for day in range(7)},
|
||
|
[],
|
||
|
None,
|
||
|
),
|
||
|
# No schedule, next change occurs after allow override starts
|
||
|
(
|
||
|
"2023-08-24 08:00",
|
||
|
False,
|
||
|
{day: [] for day in range(7)},
|
||
|
[{"start": "2023-08-26 10:00", "end": "2023-08-26 14:00", "action": True}],
|
||
|
"2023-08-26 10:00",
|
||
|
),
|
||
|
# Schedule 24/7, next change occurs after deny override starts
|
||
|
(
|
||
|
"2023-08-24 08:00",
|
||
|
True,
|
||
|
{day: [{"start": "00:00", "end": "24:00"}] for day in range(7)},
|
||
|
[{"start": "2023-08-26 10:00", "end": "2023-08-26 14:00", "action": False}],
|
||
|
"2023-08-26 10:00",
|
||
|
),
|
||
|
# No schedule, expired overrides, next change will never occur
|
||
|
(
|
||
|
"2023-08-24 08:00",
|
||
|
False,
|
||
|
{day: [] for day in range(7)},
|
||
|
[{"start": "2023-08-23 10:00", "end": "2023-08-23 14:00", "action": True}],
|
||
|
None,
|
||
|
),
|
||
|
# No schedule, next change occurs after allow override expires
|
||
|
(
|
||
|
"2023-08-24 08:00",
|
||
|
True,
|
||
|
{day: [] for day in range(7)},
|
||
|
[{"start": "2023-08-24 08:00", "end": "2023-08-26 14:00", "action": True}],
|
||
|
"2023-08-26 14:00",
|
||
|
),
|
||
|
# Next change occurs after allow override expires
|
||
|
(
|
||
|
"2023-08-24 08:00",
|
||
|
True,
|
||
|
{day: [{"start": "14:00", "end": "16:00"}] for day in range(7)},
|
||
|
[{"start": "2023-08-24 08:00", "end": "2023-08-24 10:00", "action": True}],
|
||
|
"2023-08-24 10:00",
|
||
|
),
|
||
|
# Next change occurs after schedule span ends
|
||
|
(
|
||
|
"2023-08-24 14:00",
|
||
|
True,
|
||
|
{
|
||
|
day: [{"start": "08:00", "end": "10:00"}, {"start": "14:00", "end": "16:00"}]
|
||
|
for day in range(7)
|
||
|
},
|
||
|
[{"start": "2023-08-24 08:00", "end": "2023-08-24 10:00", "action": True}],
|
||
|
"2023-08-24 16:00",
|
||
|
),
|
||
|
# Next change occurs after schedule span starts
|
||
|
(
|
||
|
"2023-08-24 13:00",
|
||
|
False,
|
||
|
{
|
||
|
day: [{"start": "08:00", "end": "10:00"}, {"start": "14:00", "end": "16:00"}]
|
||
|
for day in range(7)
|
||
|
},
|
||
|
[{"start": "2023-08-24 08:00", "end": "2023-08-24 10:00", "action": True}],
|
||
|
"2023-08-24 14:00",
|
||
|
),
|
||
|
# Next change occurs after deny override starts
|
||
|
(
|
||
|
"2023-08-24 11:00",
|
||
|
True,
|
||
|
{day: [{"start": "10:00", "end": "16:00"}] for day in range(7)},
|
||
|
[{"start": "2023-08-24 12:00", "end": "2023-08-24 14:00", "action": False}],
|
||
|
"2023-08-24 12:00",
|
||
|
),
|
||
|
# Next change occurs after allow override starts
|
||
|
(
|
||
|
"2023-08-24 10:00",
|
||
|
False,
|
||
|
{day: [{"start": "16:00", "end": "18:00"}] for day in range(7)},
|
||
|
[{"start": "2023-08-24 12:00", "end": "2023-08-24 14:00", "action": True}],
|
||
|
"2023-08-24 12:00",
|
||
|
),
|
||
|
# Next change occurs after schedule span ends after 7 days
|
||
|
(
|
||
|
"2023-08-21 13:00",
|
||
|
True,
|
||
|
{
|
||
|
0: [{"start": "00:00", "end": "04:00"}, {"start": "10:00", "end": "24:00"}],
|
||
|
1: [{"start": "00:00", "end": "24:00"}],
|
||
|
2: [{"start": "00:00", "end": "24:00"}],
|
||
|
3: [{"start": "00:00", "end": "24:00"}],
|
||
|
4: [{"start": "00:00", "end": "24:00"}],
|
||
|
5: [{"start": "00:00", "end": "24:00"}],
|
||
|
6: [{"start": "00:00", "end": "24:00"}],
|
||
|
},
|
||
|
[{"start": "2023-08-24 12:00", "end": "2023-08-24 14:00", "action": True}],
|
||
|
"2023-08-28 04:00",
|
||
|
),
|
||
|
# Next change occurs after schedule span starts after 7 days
|
||
|
(
|
||
|
"2023-08-21 13:00",
|
||
|
False,
|
||
|
{
|
||
|
0: [{"start": "00:00", "end": "04:00"}],
|
||
|
1: [],
|
||
|
2: [],
|
||
|
3: [],
|
||
|
4: [],
|
||
|
5: [],
|
||
|
6: [],
|
||
|
},
|
||
|
[{"start": "2023-08-31 12:00", "end": "2023-08-31 14:00", "action": True}],
|
||
|
"2023-08-28 00:00",
|
||
|
),
|
||
|
],
|
||
|
)
|
||
|
def test_get_next_action_change(point_in_time, action, schedules, overrides, expected_result):
|
||
|
point_in_time = datetime.strptime(point_in_time, DATETIME_FORMAT)
|
||
|
schedules = {
|
||
|
day: [MinuteSpan.from_args(**span) for span in schedule]
|
||
|
for day, schedule in schedules.items()
|
||
|
}
|
||
|
overrides = [DateTimeSpan.from_args(**o) for o in overrides]
|
||
|
|
||
|
result = app.get_next_action_change(point_in_time, action, schedules, overrides)
|
||
|
|
||
|
if expected_result:
|
||
|
expected_result = datetime.strptime(expected_result, DATETIME_FORMAT)
|
||
|
assert result == expected_result
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize(
|
||
|
"schedules, overrides, expected_result",
|
||
|
[
|
||
|
(
|
||
|
{day: [] for day in range(7)},
|
||
|
[],
|
||
|
{"action": False, "until": "", "then_until": ""},
|
||
|
),
|
||
|
(
|
||
|
{day: [{"start": "14:00", "end": "16:00"}] for day in range(7)},
|
||
|
[{"start": "2023-08-24 08:00", "end": "2023-08-24 10:00", "action": True}],
|
||
|
{"action": False, "until": "2023-08-24 14:00", "then_until": "2023-08-24 16:00"},
|
||
|
),
|
||
|
(
|
||
|
{day: [] for day in range(7)},
|
||
|
[{"start": "2023-08-24 08:00", "end": "2023-08-24 12:00", "action": True}],
|
||
|
{"action": True, "until": "2023-08-24 12:00", "then_until": ""},
|
||
|
),
|
||
|
],
|
||
|
)
|
||
|
@patch("parental_control.app.datetime", wraps=datetime)
|
||
|
def test_calculate_status(mock_datetime, schedules, overrides, expected_result):
|
||
|
mock_datetime.now.return_value = datetime(2023, 8, 24, 11)
|
||
|
schedules = {
|
||
|
day: [MinuteSpan.from_args(**span) for span in schedule]
|
||
|
for day, schedule in schedules.items()
|
||
|
}
|
||
|
overrides = [DateTimeSpan.from_args(**o) for o in overrides]
|
||
|
|
||
|
result = app.calculate_status(schedules, overrides)
|
||
|
|
||
|
assert result == expected_result
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize(
|
||
|
"schedule, span, action, expected_result",
|
||
|
[
|
||
|
# Allow span in empty schedule
|
||
|
(
|
||
|
[],
|
||
|
{"start": "09:00", "end": "11:00"},
|
||
|
True,
|
||
|
[{"start": "9:00", "end": "11:00"}],
|
||
|
),
|
||
|
# Allow span same as already existing span
|
||
|
(
|
||
|
[{"start": "9:00", "end": "11:00"}],
|
||
|
{"start": "09:00", "end": "11:00"},
|
||
|
True,
|
||
|
[{"start": "9:00", "end": "11:00"}],
|
||
|
),
|
||
|
# Allow span (starting at 0:00) prepening it before existing span
|
||
|
(
|
||
|
[{"start": "9:00", "end": "11:00"}],
|
||
|
{"start": "00:00", "end": "05:00"},
|
||
|
True,
|
||
|
[{"start": "0:00", "end": "5:00"}, {"start": "9:00", "end": "11:00"}],
|
||
|
),
|
||
|
# Allow span (ending at 24:00) appending it after existing span
|
||
|
(
|
||
|
[{"start": "9:00", "end": "11:00"}],
|
||
|
{"start": "19:00", "end": "0:00"},
|
||
|
True,
|
||
|
[{"start": "9:00", "end": "11:00"}, {"start": "19:00", "end": "24:00"}],
|
||
|
),
|
||
|
# Allow span merging existing spans together
|
||
|
(
|
||
|
[{"start": "9:00", "end": "11:00"}, {"start": "14:00", "end": "16:00"}],
|
||
|
{"start": "11:00", "end": "14:00"},
|
||
|
True,
|
||
|
[{"start": "9:00", "end": "16:00"}],
|
||
|
),
|
||
|
# Deny span from empty schedule
|
||
|
(
|
||
|
[],
|
||
|
{"start": "12:00", "end": "14:00"},
|
||
|
False,
|
||
|
[],
|
||
|
),
|
||
|
# Deny span which is already denied
|
||
|
(
|
||
|
[{"start": "9:00", "end": "11:00"}, {"start": "16:00", "end": "18:00"}],
|
||
|
{"start": "12:00", "end": "14:00"},
|
||
|
False,
|
||
|
[{"start": "9:00", "end": "11:00"}, {"start": "16:00", "end": "18:00"}],
|
||
|
),
|
||
|
# Deny span removing existing allow spans
|
||
|
(
|
||
|
[{"start": "9:00", "end": "11:00"}, {"start": "16:00", "end": "18:00"}],
|
||
|
{"start": "0:00", "end": "0:00"},
|
||
|
False,
|
||
|
[],
|
||
|
),
|
||
|
# Deny span within allow span, splitting the allow span
|
||
|
(
|
||
|
[{"start": "9:00", "end": "16:00"}],
|
||
|
{"start": "11:00", "end": "14:00"},
|
||
|
False,
|
||
|
[{"start": "9:00", "end": "11:00"}, {"start": "14:00", "end": "16:00"}],
|
||
|
),
|
||
|
# Deny span moving end and start of existing allow spans
|
||
|
(
|
||
|
[{"start": "9:00", "end": "11:00"}, {"start": "16:00", "end": "18:00"}],
|
||
|
{"start": "10:00", "end": "17:00"},
|
||
|
False,
|
||
|
[{"start": "9:00", "end": "10:00"}, {"start": "17:00", "end": "18:00"}],
|
||
|
),
|
||
|
],
|
||
|
)
|
||
|
def test_update_schedule(schedule, span, action, expected_result):
|
||
|
schedule = [MinuteSpan.from_args(**s) for s in schedule]
|
||
|
span = MinuteSpan.from_args(**span)
|
||
|
|
||
|
app.update_schedule(schedule, span, action)
|
||
|
|
||
|
expected_result = [MinuteSpan.from_args(**s) for s in expected_result]
|
||
|
assert schedule == expected_result
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize(
|
||
|
"overrides, start, end, action, expected_result",
|
||
|
[
|
||
|
# Add override into empty list of overrides
|
||
|
(
|
||
|
[],
|
||
|
{"start": "2023-08-24 08:00", "end": "2023-08-24 10:00", "action": True},
|
||
|
[{"start": "2023-08-24 08:00", "end": "2023-08-24 10:00", "action": True}],
|
||
|
),
|
||
|
# Add override into list of overrides
|
||
|
(
|
||
|
[
|
||
|
{"start": "2023-08-24 00:00", "end": "2023-08-24 06:00", "action": True},
|
||
|
{"start": "2023-08-24 15:00", "end": "2023-08-24 20:00", "action": True},
|
||
|
],
|
||
|
{"start": "2023-08-24 08:00", "end": "2023-08-24 10:00", "action": True},
|
||
|
[
|
||
|
{"start": "2023-08-24 00:00", "end": "2023-08-24 06:00", "action": True},
|
||
|
{"start": "2023-08-24 08:00", "end": "2023-08-24 10:00", "action": True},
|
||
|
{"start": "2023-08-24 15:00", "end": "2023-08-24 20:00", "action": True},
|
||
|
],
|
||
|
),
|
||
|
# Add override removing another override spanning over smaller period
|
||
|
(
|
||
|
[
|
||
|
{"start": "2023-08-24 00:00", "end": "2023-08-24 06:00", "action": True},
|
||
|
{"start": "2023-08-24 15:00", "end": "2023-08-24 20:00", "action": True},
|
||
|
],
|
||
|
{"start": "2023-08-24 15:00", "end": "2023-08-24 22:00", "action": False},
|
||
|
[
|
||
|
{"start": "2023-08-24 00:00", "end": "2023-08-24 06:00", "action": True},
|
||
|
{"start": "2023-08-24 15:00", "end": "2023-08-24 22:00", "action": False},
|
||
|
],
|
||
|
),
|
||
|
# Add override within existing override span, splitting it
|
||
|
(
|
||
|
[{"start": "2023-08-24 08:00", "end": "2023-08-24 20:00", "action": False}],
|
||
|
{"start": "2023-08-24 16:00", "end": "2023-08-24 18:00", "action": True},
|
||
|
[
|
||
|
{"start": "2023-08-24 08:00", "end": "2023-08-24 16:00", "action": False},
|
||
|
{"start": "2023-08-24 16:00", "end": "2023-08-24 18:00", "action": True},
|
||
|
{"start": "2023-08-24 18:00", "end": "2023-08-24 20:00", "action": False},
|
||
|
],
|
||
|
),
|
||
|
# Add override moving end and start of existing overrides
|
||
|
(
|
||
|
[
|
||
|
{"start": "2023-08-24 00:00", "end": "2023-08-24 06:00", "action": True},
|
||
|
{"start": "2023-08-24 15:00", "end": "2023-08-24 20:00", "action": True},
|
||
|
],
|
||
|
{"start": "2023-08-24 04:00", "end": "2023-08-24 18:00", "action": False},
|
||
|
[
|
||
|
{"start": "2023-08-24 00:00", "end": "2023-08-24 04:00", "action": True},
|
||
|
{"start": "2023-08-24 04:00", "end": "2023-08-24 18:00", "action": False},
|
||
|
{"start": "2023-08-24 18:00", "end": "2023-08-24 20:00", "action": True},
|
||
|
],
|
||
|
),
|
||
|
# Add override merging existing overrides
|
||
|
(
|
||
|
[
|
||
|
{"start": "2023-08-24 00:00", "end": "2023-08-24 06:00", "action": True},
|
||
|
{"start": "2023-08-24 15:00", "end": "2023-08-24 20:00", "action": True},
|
||
|
],
|
||
|
{"start": "2023-08-24 04:00", "end": "2023-08-24 18:00", "action": True},
|
||
|
[{"start": "2023-08-24 00:00", "end": "2023-08-24 20:00", "action": True}],
|
||
|
),
|
||
|
],
|
||
|
)
|
||
|
def test_update_overrides(overrides, span, expected_result):
|
||
|
overrides = [DateTimeSpan.from_args(**o) for o in overrides]
|
||
|
span = DateTimeSpan.from_args(**span)
|
||
|
|
||
|
app.update_overrides(overrides, span)
|
||
|
|
||
|
expected_result = [DateTimeSpan.from_args(**o) for o in expected_result]
|
||
|
assert overrides == expected_result
|