Skip to content

Commit f446d51

Browse files
committed
fix(monitor): the All timeline spans the data, not a decade
Found by checking the Month and All periods, which the smoke tests had not touched. All drew an x-axis running from roughly 2016 with every real sample crushed into a sliver at the right edge - the one period whose entire job is to show everything was the only one that showed nothing. The database is fine; the earliest row is 2026-06-27. The range was wrong. get_start_time() falls back to `now - 10 years` for TIMELINE_ALL when it is not told the earliest timestamp, and graph_host only looked that up for TIMELINE_SYSTEM_UPTIME: if period_key == "TIMELINE_SYSTEM_UPTIME" and self._cached_boot_time is None: so All always took the fallback. The query it needed already existed and worked. The Monitor's Overview tab had always asked for both periods; this path had drifted from it - the same shape as the iGPU VRAM drift in #269, where one call site was right and another had not kept up. The lookup now latches on the ATTEMPT rather than the cached value. An empty database returns None legitimately, and _time_range runs on every refresh and realtime tick, so gating on the result would mean a UI-thread database call per tick forever. Verified live: All now spans 2026-07-01 to today with the whole history legible, on both the Network and Hardware tabs. Month was checked too and was already correct.
1 parent 8156328 commit f446d51

2 files changed

Lines changed: 83 additions & 2 deletions

File tree

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
"""
2+
The "All" period has to span the data that exists, not a decade.
3+
4+
`HistoryPeriodConstants.get_start_time` falls back to `now - 10 years` for TIMELINE_ALL when it is
5+
not told the earliest row in the database:
6+
7+
if earliest_db:
8+
return earliest_db
9+
return now - timedelta(days=365*10)
10+
11+
That fallback is reasonable in isolation. What was not reasonable is that the Monitor's graph only
12+
looked the earliest row up for **SYSTEM UPTIME**, so "All" always took the fallback: an axis running
13+
from roughly 2016 with every real sample crushed into a sliver at the right edge. The one period
14+
whose entire job is to show everything was the only one that showed nothing.
15+
16+
The Overview tab had always asked for both periods; `graph_host` had drifted from it.
17+
"""
18+
19+
from datetime import datetime, timedelta
20+
21+
from netspeedtray import constants
22+
23+
hp = constants.data.history_period
24+
25+
26+
def test_all_starts_at_the_earliest_row_when_told():
27+
now = datetime(2026, 8, 23, 12, 0, 0)
28+
earliest = datetime(2026, 6, 27, 20, 0, 0)
29+
assert hp.get_start_time("TIMELINE_ALL", now, earliest_db=earliest) == earliest
30+
31+
32+
def test_all_without_the_earliest_row_spans_a_decade():
33+
"""Pins the fallback that made this visible - and why it must not be the normal path."""
34+
now = datetime(2026, 8, 23, 12, 0, 0)
35+
start = hp.get_start_time("TIMELINE_ALL", now, earliest_db=None)
36+
assert start is not None
37+
assert (now - start).days >= 365 * 9, "the fallback is what produced the ~2016 axis"
38+
39+
40+
def test_graph_host_requests_the_earliest_row_for_all():
41+
"""The regression itself: the lookup must be gated on ALL as well as SYSTEM UPTIME.
42+
43+
Asserted against the source because the alternative is standing up a Monitor window, a main
44+
widget and a live database to observe one boolean.
45+
"""
46+
import inspect
47+
from netspeedtray.views.monitor import graph_host
48+
49+
src = inspect.getsource(graph_host.GraphHost._time_range)
50+
assert "TIMELINE_ALL" in src, (
51+
"graph_host._time_range no longer asks for the earliest row on the All period - the axis "
52+
"will fall back to a ten-year span")
53+
assert "TIMELINE_SYSTEM_UPTIME" in src, "the uptime period must keep working too"
54+
55+
56+
def test_the_lookup_latches_on_the_attempt_not_the_result():
57+
"""An empty database returns None legitimately; retrying would be a UI-thread DB call per tick."""
58+
import inspect
59+
from netspeedtray.views.monitor import graph_host
60+
61+
src = inspect.getsource(graph_host.GraphHost._time_range)
62+
assert "_earliest_db_fetched" in src, (
63+
"the lookup is gated on a cached VALUE rather than on whether it was attempted, so an empty "
64+
"database would re-query on every refresh and realtime tick")
65+
66+
67+
def test_other_periods_are_unaffected():
68+
now = datetime(2026, 8, 23, 12, 0, 0)
69+
assert hp.get_start_time("TIMELINE_WEEK", now) == now - timedelta(days=7)
70+
assert hp.get_start_time("TIMELINE_MONTH", now) == now - timedelta(days=30)
71+
assert hp.get_start_time("TIMELINE_24_HOURS", now) == now - timedelta(days=1)

src/netspeedtray/views/monitor/graph_host.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ def __init__(self, main_widget, config: Dict[str, Any], i18n,
104104
self._accept_from_seq = 0 # drop in-flight results from a previous stat (cross-tab)
105105
self._cached_boot_time = None # fetched once for the uptime range (mirrors GraphWindow)
106106
self._cached_earliest_db = None
107+
self._earliest_db_fetched = False
107108

108109
# shims the coordinator drives (matplotlib-free)
109110
self.ui = _UiShim()
@@ -304,9 +305,18 @@ def _time_range(self):
304305
# Fetch boot/earliest ONCE for the uptime range. These are UI-thread DB calls and _time_range
305306
# runs on every refresh + realtime tick - GraphWindow caches them the same way (and the cache
306307
# is naturally fresh each session, since GraphHost is recreated per Monitor window).
307-
if period_key == "TIMELINE_SYSTEM_UPTIME" and self._cached_boot_time is None:
308+
# ALL needs the earliest row too, not just SYSTEM UPTIME. Without it, get_start_time() falls
309+
# back to `now - 10 years`, so "All" drew an axis from ~2016 with every real sample crushed
310+
# into a sliver at the right edge - the one period whose whole job is to show everything was
311+
# the one that showed nothing. The Overview tab already asked for both; this path had drifted.
312+
if period_key in ("TIMELINE_SYSTEM_UPTIME", "TIMELINE_ALL") and not self._earliest_db_fetched:
313+
# Latch on the ATTEMPT, not on the result: an empty database legitimately returns None,
314+
# and this runs on every refresh and realtime tick - retrying would be a UI-thread DB
315+
# call per tick, forever.
316+
self._earliest_db_fetched = True
308317
try:
309-
self._cached_boot_time = GraphLogic.get_boot_time()
318+
if period_key == "TIMELINE_SYSTEM_UPTIME":
319+
self._cached_boot_time = GraphLogic.get_boot_time()
310320
self._cached_earliest_db = self._main_widget.widget_state.get_earliest_data_timestamp()
311321
except Exception:
312322
self._cached_boot_time = self._cached_earliest_db = None

0 commit comments

Comments
 (0)