Files
upstream-aw-core/tests/test_flood.py
upstream-mirror 8757f2fd5e
Some checks failed
Build / Test on ubuntu-latest, py-3.8 (push) Successful in 4m21s
Lint / lint (push) Successful in 25s
Build / Test on macOS-latest, py-3.8 (push) Has been cancelled
Build / Test on windows-latest, py-3.8 (push) Has been cancelled
CodeQL / Analyze (python) (push) Failing after 1s
snapshot of https://github.com/ActivityWatch/aw-core.git (master, 2026-09-16; blobs > 131072B omitted)
2026-09-16 00:17:07 +05:00

168 lines
5.4 KiB
Python

from datetime import datetime, timedelta, timezone
from aw_core.models import Event
from aw_transform import flood
now = datetime.now(tz=timezone.utc)
td1s = timedelta(seconds=1)
def test_flood_differing_data_meet_at_gap_midpoint():
events = [
Event(timestamp=now, duration=10, data={"a": 0}),
Event(timestamp=now + 14 * td1s, duration=5, data={"b": 1}),
]
flooded = flood(events)
assert flooded == [
Event(timestamp=now, duration=12, data={"a": 0}),
Event(timestamp=now + 12 * td1s, duration=7, data={"b": 1}),
]
def test_flood_forward_merge():
events = [
Event(timestamp=now, duration=10),
Event(timestamp=now + 15 * td1s, duration=5),
]
flooded = flood(events)
assert len(flooded) == 1
assert flooded[0].duration == timedelta(seconds=20)
def test_flood_backward_merge():
events = [
Event(timestamp=now, duration=5),
Event(timestamp=now + 10 * td1s, duration=10),
]
flooded = flood(events)
assert len(flooded) == 1
assert flooded[0].duration == timedelta(seconds=20)
def test_flood_negative_gap_same_data():
events = [
Event(timestamp=now, duration=100, data={"a": 0}),
Event(timestamp=now, duration=5, data={"a": 0}),
]
flooded = flood(events)
total_duration = sum((e.duration for e in flooded), timedelta(0))
assert len(flooded) == 1
assert total_duration == timedelta(seconds=100)
def test_flood_negative_gap_differing_data():
events = [
Event(timestamp=now, duration=5, data={"a": 0}),
Event(timestamp=now, duration=100, data={"b": 1}),
]
flooded = flood(events)
assert flooded == [events[1]]
def test_flood_zero_duration_chain_does_not_leave_overlaps():
events = [
Event(timestamp=now, duration=0, data={"title": "first"}),
Event(timestamp=now, duration=1, data={"title": "first"}),
Event(timestamp=now, duration=1, data={"title": "second"}),
]
flooded = flood(events)
assert flooded == [events[2]]
assert all(
previous.timestamp + previous.duration <= current.timestamp
for previous, current in zip(flooded, flooded[1:])
)
def test_flood_normalization_preserves_non_overlapping_tail():
events = [
Event(timestamp=now, duration=10, data={"title": "first"}),
Event(timestamp=now + 5 * td1s, duration=2, data={"title": "second"}),
Event(timestamp=now + 12 * td1s, duration=1, data={"title": "third"}),
]
flooded = flood(events)
assert flooded == [
Event(timestamp=now, duration=5, data={"title": "first"}),
Event(timestamp=now + 5 * td1s, duration=4.5, data={"title": "second"}),
Event(timestamp=now + 9.5 * td1s, duration=3.5, data={"title": "third"}),
]
def test_flood_normalization_merges_same_data_after_zero_duration_event():
events = [
Event(timestamp=now, duration=0, data={"title": "zero"}),
Event(timestamp=now, duration=10, data={"title": "same"}),
Event(timestamp=now + 5 * td1s, duration=10, data={"title": "same"}),
]
flooded = flood(events)
assert flooded == [Event(timestamp=now, duration=15, data={"title": "same"})]
def test_flood_negative_small_gap_differing_data():
events = [
Event(timestamp=now, duration=100, data={"b": 1}),
Event(timestamp=now + 99.99 * td1s, duration=100, data={"a": 0}),
]
flooded = flood(events)
duration = sum((e.duration for e in flooded), timedelta(0))
assert duration == timedelta(seconds=100 + 99.99)
def test_flood_with_custom_pulsetime():
# Events with a 30s gap between them (simulating 30s poll_time)
events = [
Event(timestamp=now, duration=5, data={"a": 0}),
Event(timestamp=now + 35 * td1s, duration=5, data={"b": 1}),
]
# Default pulsetime=5: gap (30s) > pulsetime, so no flooding
flooded_default = flood(events)
assert len(flooded_default) == 2
total_duration_default = sum((e.duration for e in flooded_default), timedelta(0))
assert total_duration_default == timedelta(seconds=10)
# pulsetime=31: gap (30s) <= pulsetime, so both events extend to midpoint
flooded_custom = flood(events, pulsetime=31)
assert flooded_custom == [
Event(timestamp=now, duration=20, data={"a": 0}),
Event(timestamp=now + 20 * td1s, duration=20, data={"b": 1}),
]
def test_flood_idempotent():
"""flood() applied repeatedly should produce the same result."""
events = [
# slight overlap, same data
Event(timestamp=now, duration=10, data={"a": 0}),
Event(timestamp=now + 9 * td1s, duration=5, data={"a": 0}),
# different data, no overlap
Event(timestamp=now + 15 * td1s, duration=5, data={"b": 0}),
]
flood_first = flood(events, pulsetime=0)
flooded = flood_first
for _ in range(2):
flooded = flood(flooded, pulsetime=0)
assert flood_first == flooded
assert sum((e.duration for e in flooded), timedelta(0)) == 19 * td1s
def test_flood_unsafe_gap():
"""Overlapping events with differing data must not double-count time."""
events = [
Event(timestamp=now, duration=10, data={"a": 0}),
Event(timestamp=now + 9 * td1s, duration=5, data={"b": 0}),
]
flooded = flood(events, pulsetime=0)
# Total duration must not exceed the span covered by the two events
assert sum((e.duration for e in flooded), timedelta(0)) == 14 * td1s