Files
hassio-addons-avm/hassio-google-drive-backup/tests/test_scheme.py

443 lines
14 KiB
Python

from datetime import datetime, timedelta
import pytest
from dateutil.tz import tzutc
from pytest import fail
from backup.model import GenConfig, GenerationalScheme, DummyBackup, Backup
from backup.time import Time
def test_timezone(time) -> None:
assert time.local_tz is not None
def test_trivial(time) -> None:
config = GenConfig(days=1)
scheme = GenerationalScheme(time, config, count=0)
backups = [
makeBackup("single", time.local(1928, 12, 6))
]
assert scheme.getOldest(backups)[1].date() == time.local(1928, 12, 6)
def test_trivial_empty(time):
config = GenConfig(days=1)
scheme = GenerationalScheme(time, config, count=0)
assert scheme.getOldest([])[1] is None
def test_trivial_oldest(time: Time) -> None:
config = GenConfig(days=1)
scheme = GenerationalScheme(time, config, count=0)
backups = [
makeBackup("test", time.local(1985, 12, 6, 10)),
makeBackup("test", time.local(1985, 12, 6, 12)),
makeBackup("test", time.local(1985, 12, 6, 13))
]
assertRemovalOrder(scheme, backups, [
time.local(1985, 12, 6, 10),
time.local(1985, 12, 6, 12),
time.local(1985, 12, 6, 13)
])
def test_duplicate_weeks(time):
config = GenConfig(weeks=1, day_of_week='wed')
scheme = GenerationalScheme(time, config, count=0)
backups = [
makeBackup("test", time.local(1985, 12, 5)),
makeBackup("test", time.local(1985, 12, 4)),
makeBackup("test", time.local(1985, 12, 1)),
makeBackup("test", time.local(1985, 12, 2))
]
assertRemovalOrder(scheme, backups, [
time.local(1985, 12, 1),
time.local(1985, 12, 2),
time.local(1985, 12, 5),
time.local(1985, 12, 4)
])
def test_duplicate_months(time) -> None:
config = GenConfig(months=2, day_of_month=15)
scheme = GenerationalScheme(time, config, count=0)
backups = [
makeBackup("test", time.local(1985, 12, 6)),
makeBackup("test", time.local(1985, 12, 15)),
makeBackup("test", time.local(1985, 11, 20)),
makeBackup("test", time.local(1985, 11, 15))
]
assertRemovalOrder(scheme, backups, [
time.local(1985, 11, 20),
time.local(1985, 12, 6),
time.local(1985, 11, 15),
time.local(1985, 12, 15)
])
def test_duplicate_years(time):
config = GenConfig(years=2, day_of_year=1)
scheme = GenerationalScheme(time, config, count=0)
backups = [
makeBackup("test", time.local(1985, 12, 31)),
makeBackup("test", time.local(1985, 1, 1)),
makeBackup("test", time.local(1984, 12, 31)),
makeBackup("test", time.local(1984, 1, 1))
]
assertRemovalOrder(scheme, backups, [
time.local(1984, 12, 31),
time.local(1985, 12, 31),
time.local(1984, 1, 1),
time.local(1985, 1, 1)
])
def test_removal_order(time) -> None:
config = GenConfig(days=5, weeks=2, months=2, years=2,
day_of_week='mon', day_of_month=15, day_of_year=1)
scheme = GenerationalScheme(time, config, count=0)
backups = [
# 5 days, week 1
makeBackup("test", time.local(1985, 12, 7)), # day 1
makeBackup("test", time.local(1985, 12, 6)), # day 2
makeBackup("test", time.local(1985, 12, 5)), # day 3
makeBackup("test", time.local(1985, 12, 4)), # day 4
makeBackup("test", time.local(1985, 12, 3)), # day 5
makeBackup("test", time.local(1985, 12, 1)), # 1st week pref
# week 2
makeBackup("test", time.local(1985, 11, 25)), # 1st month pref
# month2
makeBackup("test", time.local(1985, 11, 15)), # 2nd month pref
# year 1
makeBackup("test", time.local(1985, 1, 1)), # 1st year preference
makeBackup("test", time.local(1985, 1, 2)),
# year 2
makeBackup("test", time.local(1984, 6, 1)), # 2nd year pref
makeBackup("test", time.local(1984, 7, 1)),
# year 3
makeBackup("test", time.local(1983, 1, 1)),
]
assertRemovalOrder(scheme, backups, [
time.local(1983, 1, 1),
time.local(1984, 7, 1),
time.local(1985, 1, 2),
time.local(1984, 6, 1),
time.local(1985, 1, 1),
time.local(1985, 11, 15),
time.local(1985, 11, 25),
time.local(1985, 12, 1),
time.local(1985, 12, 3),
time.local(1985, 12, 4),
time.local(1985, 12, 5),
time.local(1985, 12, 6),
time.local(1985, 12, 7)
])
@pytest.mark.timeout(60)
def test_simulate_daily_backup_for_4_years(time):
config = GenConfig(days=4, weeks=4, months=4, years=4,
day_of_week='mon', day_of_month=1, day_of_year=1)
scheme = GenerationalScheme(time, config, count=16)
backups = simulate(time.local(2019, 1, 1),
time.local(2022, 12, 31),
scheme)
assertRemovalOrder(GenerationalScheme(time, config, count=0), backups, [
# 4 years
time.local(2019, 1, 1),
time.local(2020, 1, 1),
time.local(2021, 1, 1),
time.local(2022, 1, 1),
# 4 months
time.local(2022, 9, 1),
time.local(2022, 10, 1),
time.local(2022, 11, 1),
time.local(2022, 12, 1),
# 4 weeks
time.local(2022, 12, 5),
time.local(2022, 12, 12),
time.local(2022, 12, 19),
time.local(2022, 12, 26),
# 4 days
time.local(2022, 12, 28),
time.local(2022, 12, 29),
time.local(2022, 12, 30),
time.local(2022, 12, 31)
])
@pytest.mark.timeout(60)
def test_simulate_agressive_daily_backup_for_4_years(time):
config = GenConfig(days=4, weeks=4, months=4, years=4,
day_of_week='mon', day_of_month=1, day_of_year=1, aggressive=True)
scheme = GenerationalScheme(time, config, count=16)
backups = simulate(time.local(2019, 1, 1),
time.local(2022, 12, 31),
scheme)
assertRemovalOrder(GenerationalScheme(time, config, count=0), backups, [
# 4 years
time.local(2019, 1, 1),
time.local(2020, 1, 1),
time.local(2021, 1, 1),
time.local(2022, 1, 1),
# 4 months
time.local(2022, 9, 1),
time.local(2022, 10, 1),
time.local(2022, 11, 1),
time.local(2022, 12, 1),
# 4 weeks
time.local(2022, 12, 5),
time.local(2022, 12, 12),
time.local(2022, 12, 19),
time.local(2022, 12, 26),
# 4 days
time.local(2022, 12, 28),
time.local(2022, 12, 29),
time.local(2022, 12, 30),
time.local(2022, 12, 31),
])
def test_count_limit(time):
config = GenConfig(years=2, day_of_year=1)
scheme = GenerationalScheme(time, config, count=1)
backups = [
makeBackup("test", time.local(1985, 1, 1)),
makeBackup("test", time.local(1984, 1, 1))
]
assertRemovalOrder(scheme, backups, [
time.local(1984, 1, 1)
])
def test_aggressive_removal_below_limit(time):
config = GenConfig(years=2, day_of_year=1, aggressive=True)
scheme = GenerationalScheme(time, config, count=5)
backups = [
makeBackup("test", time.local(1985, 1, 1)),
makeBackup("test", time.local(1985, 1, 2))
]
assertRemovalOrder(scheme, backups, [
time.local(1985, 1, 2)
])
def test_aggressive_removal_at_limit_ok(time):
config = GenConfig(years=2, day_of_year=1, aggressive=True)
scheme = GenerationalScheme(time, config, count=2)
backups = [
makeBackup("test", time.local(1985, 1, 1)),
makeBackup("test", time.local(1984, 1, 1))
]
assertRemovalOrder(scheme, backups, [])
def test_aggressive_removal_over_limit(time):
config = GenConfig(years=2, day_of_year=1, aggressive=True)
scheme = GenerationalScheme(time, config, count=2)
backups = [
makeBackup("test", time.local(1985, 1, 1)),
makeBackup("test", time.local(1984, 1, 1)),
makeBackup("test", time.local(1983, 1, 1)),
makeBackup("test", time.local(1983, 1, 2))
]
assertRemovalOrder(scheme, backups, [
time.local(1983, 1, 1),
time.local(1983, 1, 2)
])
def test_removal_order_week(time: Time):
config = GenConfig(weeks=1, day_of_week='wed', aggressive=True)
scheme = GenerationalScheme(time, config, count=1)
backups = [
makeBackup("test", time.local(2019, 10, 28)),
makeBackup("test", time.local(2019, 10, 29)),
makeBackup("test", time.local(2019, 10, 30, 1)),
makeBackup("test", time.local(2019, 10, 30, 2)),
makeBackup("test", time.local(2019, 10, 31)),
makeBackup("test", time.local(2019, 11, 1)),
makeBackup("test", time.local(2019, 11, 2)),
makeBackup("test", time.local(2019, 11, 3)),
]
assertRemovalOrder(scheme, backups, [
time.local(2019, 10, 28),
time.local(2019, 10, 29),
time.local(2019, 10, 30, 1),
time.local(2019, 10, 31),
time.local(2019, 11, 1),
time.local(2019, 11, 2),
time.local(2019, 11, 3)
])
def test_removal_order_month(time):
config = GenConfig(months=1, day_of_month=20, aggressive=True)
scheme = GenerationalScheme(time, config, count=1)
backups = [
makeBackup("test", time.local(2019, 1, 1)),
makeBackup("test", time.local(2019, 1, 2)),
makeBackup("test", time.local(2019, 1, 20, 1)),
makeBackup("test", time.local(2019, 1, 20, 2)),
makeBackup("test", time.local(2019, 1, 21)),
makeBackup("test", time.local(2019, 1, 25)),
makeBackup("test", time.local(2019, 1, 26)),
makeBackup("test", time.local(2019, 1, 27)),
]
assertRemovalOrder(scheme, backups, [
time.local(2019, 1, 1),
time.local(2019, 1, 2),
time.local(2019, 1, 20, 1),
time.local(2019, 1, 21),
time.local(2019, 1, 25),
time.local(2019, 1, 26),
time.local(2019, 1, 27)
])
def test_removal_order_many_months(time):
config = GenConfig(months=70, day_of_month=20, aggressive=True)
scheme = GenerationalScheme(time, config, count=10)
backups = [
makeBackup("test", time.local(2019, 7, 20)), # preferred
makeBackup("test", time.local(2018, 7, 18)), # preferred
makeBackup("test", time.local(2018, 7, 21)),
makeBackup("test", time.local(2017, 1, 19)),
makeBackup("test", time.local(2017, 1, 20)), # preferred
makeBackup("test", time.local(2017, 1, 31)),
makeBackup("test", time.local(2016, 12, 1)), # preferred
makeBackup("test", time.local(2014, 1, 31)),
makeBackup("test", time.local(2014, 1, 1)), # preferred
]
assertRemovalOrder(scheme, backups, [
time.local(2014, 1, 31),
time.local(2017, 1, 19),
time.local(2017, 1, 31),
time.local(2018, 7, 21),
])
def test_removal_order_years(time):
config = GenConfig(years=2, day_of_year=15, aggressive=True)
scheme = GenerationalScheme(time, config, count=10)
backups = [
makeBackup("test", time.local(2019, 2, 15)),
makeBackup("test", time.local(2019, 1, 15)), # keep
makeBackup("test", time.local(2018, 1, 14)),
makeBackup("test", time.local(2018, 1, 15)), # keep
makeBackup("test", time.local(2018, 1, 16)),
makeBackup("test", time.local(2017, 1, 15)),
]
assertRemovalOrder(scheme, backups, [
time.local(2017, 1, 15),
time.local(2018, 1, 14),
time.local(2018, 1, 16),
time.local(2019, 2, 15),
])
@pytest.mark.asyncio
async def test_ignored_generational_labels(time):
config = GenConfig(days=2)
scheme = GenerationalScheme(time, config, count=10)
backup1 = makeBackup("test", time.local(2019, 2, 15))
backup2 = makeBackup("test", time.local(2019, 2, 14))
backup3 = makeBackup("test", time.local(2019, 2, 13), ignore=True)
backups = [backup1, backup2, backup3]
scheme.handleNaming(backups)
assert backup1.getStatusDetail() == ['Day 1 of 2']
assert backup2.getStatusDetail() == ['Day 2 of 2']
assert backup3.getStatusDetail() is None
def getRemovalOrder(scheme, toCheck):
backups = list(toCheck)
removed = []
while True:
oldest = scheme.getOldest(backups)
if not oldest:
break
removed.append(oldest.date())
backups.remove(oldest)
return removed
def assertRemovalOrder(scheme, toCheck, expected):
backups = list(toCheck)
removed = []
index = 0
time = scheme.time
while True:
reason, oldest = scheme.getOldest(backups)
if index >= len(expected):
if oldest is not None:
fail("at index {0}, expected 'None' but got {1}".format(
index, time.toLocal(oldest.date())))
break
if oldest.date() != expected[index]:
fail("at index {0}, expected {1} but got {2}".format(
index, time.toLocal(expected[index]), time.toLocal(oldest.date())))
removed.append(oldest.date())
backups.remove(oldest)
index += 1
return removed
def makeBackup(slug, date, name=None, ignore=False) -> Backup:
if not name:
name = slug
return DummyBackup(name, date.astimezone(tzutc()), "src", slug, ignore=ignore)
def simulate(start: datetime, end: datetime, scheme: GenerationalScheme, backups=[]):
today = start
while today <= end:
backups.append(makeBackup("test", today))
test = scheme.getOldest(backups)
if test is None:
pass
reason, oldest = test
while oldest is not None:
backups.remove(oldest)
test = scheme.getOldest(backups)
if test is None:
pass
reason, oldest = test
today = today + timedelta(hours=27)
today = scheme.time.local(today.year, today.month, today.day)
return backups