mirror of
https://github.com/Horizontal42/netsleuth.git
synced 2026-11-10 20:22:03 +03:00
Refactor measure_with_bufferbloat to use BufferbloatMeasureConfig
Encapsulated arguments of `measure_with_bufferbloat` into a frozen dataclass `BufferbloatMeasureConfig` to reduce function signature complexity. Updated callers in CLI and tests accordingly. Co-authored-by: Horizontal42 <75383519+Horizontal42@users.noreply.github.com>
This commit is contained in:
parent
8c99ac78b7
commit
7f2ab2605d
3 changed files with 44 additions and 31 deletions
|
|
@ -516,6 +516,7 @@ async def _speed_section(client, settings: Settings, options: Options, idle_rtt_
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
from netsleuth.speed import (
|
from netsleuth.speed import (
|
||||||
|
BufferbloatMeasureConfig,
|
||||||
measure_with_bufferbloat,
|
measure_with_bufferbloat,
|
||||||
run_speed_cascade,
|
run_speed_cascade,
|
||||||
tier_cloudflare,
|
tier_cloudflare,
|
||||||
|
|
@ -559,12 +560,14 @@ async def _speed_section(client, settings: Settings, options: Options, idle_rtt_
|
||||||
|
|
||||||
result = await measure_with_bufferbloat(
|
result = await measure_with_bufferbloat(
|
||||||
result,
|
result,
|
||||||
|
config=BufferbloatMeasureConfig(
|
||||||
idle_rtt_ms=idle_rtt_ms if result.idle_rtt_ms is None else result.idle_rtt_ms,
|
idle_rtt_ms=idle_rtt_ms if result.idle_rtt_ms is None else result.idle_rtt_ms,
|
||||||
bands=settings.thresholds.bufferbloat_ms,
|
bands=settings.thresholds.bufferbloat_ms,
|
||||||
run_download=_saturate_down,
|
run_download=_saturate_down,
|
||||||
run_upload=_saturate_up,
|
run_upload=_saturate_up,
|
||||||
probe=_probe,
|
probe=_probe,
|
||||||
interval=cfg.bufferbloat_probe_interval_seconds,
|
interval=cfg.bufferbloat_probe_interval_seconds,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
return ModuleResult(name="speed", status="ok", data=result)
|
return ModuleResult(name="speed", status="ok", data=result)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ def bufferbloat_delta(idle_rtt_ms: float | None, loaded_rtts_ms: list[float]) ->
|
||||||
import asyncio
|
import asyncio
|
||||||
import time
|
import time
|
||||||
from collections.abc import Awaitable, Callable
|
from collections.abc import Awaitable, Callable
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from netsleuth.models import SpeedResult, TierAttempt
|
from netsleuth.models import SpeedResult, TierAttempt
|
||||||
|
|
||||||
|
|
@ -241,6 +242,16 @@ from netsleuth.interpret import grade_bufferbloat
|
||||||
from netsleuth.stats import rtt_stats
|
from netsleuth.stats import rtt_stats
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class BufferbloatMeasureConfig:
|
||||||
|
idle_rtt_ms: float | None
|
||||||
|
bands: BufferbloatBands
|
||||||
|
run_download: Callable[[], Awaitable[None]]
|
||||||
|
run_upload: Callable[[], Awaitable[None]]
|
||||||
|
probe: Callable[[], Awaitable[float | None]]
|
||||||
|
interval: float
|
||||||
|
|
||||||
|
|
||||||
async def probe_while(
|
async def probe_while(
|
||||||
coro: Awaitable[None],
|
coro: Awaitable[None],
|
||||||
probe: Callable[[], Awaitable[float | None]],
|
probe: Callable[[], Awaitable[float | None]],
|
||||||
|
|
@ -259,25 +270,20 @@ async def probe_while(
|
||||||
|
|
||||||
async def measure_with_bufferbloat(
|
async def measure_with_bufferbloat(
|
||||||
result: SpeedResult,
|
result: SpeedResult,
|
||||||
idle_rtt_ms: float | None,
|
config: BufferbloatMeasureConfig,
|
||||||
bands: BufferbloatBands,
|
|
||||||
run_download: Callable[[], Awaitable[None]],
|
|
||||||
run_upload: Callable[[], Awaitable[None]],
|
|
||||||
probe: Callable[[], Awaitable[float | None]],
|
|
||||||
interval: float,
|
|
||||||
) -> SpeedResult:
|
) -> SpeedResult:
|
||||||
# This is the one place a measurement is allowed to overlap another: the
|
# This is the one place a measurement is allowed to overlap another: the
|
||||||
# whole point is to see what latency does while the link is saturated.
|
# whole point is to see what latency does while the link is saturated.
|
||||||
down_samples = await probe_while(run_download(), probe, interval)
|
down_samples = await probe_while(config.run_download(), config.probe, config.interval)
|
||||||
up_samples = await probe_while(run_upload(), probe, interval)
|
up_samples = await probe_while(config.run_upload(), config.probe, config.interval)
|
||||||
result.idle_rtt_ms = idle_rtt_ms
|
result.idle_rtt_ms = config.idle_rtt_ms
|
||||||
result.loaded_rtt_down_ms = rtt_stats(list(down_samples)).avg_ms
|
result.loaded_rtt_down_ms = rtt_stats(list(down_samples)).avg_ms
|
||||||
result.loaded_rtt_up_ms = rtt_stats(list(up_samples)).avg_ms
|
result.loaded_rtt_up_ms = rtt_stats(list(up_samples)).avg_ms
|
||||||
result.bufferbloat_down_ms = bufferbloat_delta(idle_rtt_ms, down_samples)
|
result.bufferbloat_down_ms = bufferbloat_delta(config.idle_rtt_ms, down_samples)
|
||||||
result.bufferbloat_up_ms = bufferbloat_delta(idle_rtt_ms, up_samples)
|
result.bufferbloat_up_ms = bufferbloat_delta(config.idle_rtt_ms, up_samples)
|
||||||
worst_delta = max(
|
worst_delta = max(
|
||||||
[d for d in (result.bufferbloat_down_ms, result.bufferbloat_up_ms) if d is not None],
|
[d for d in (result.bufferbloat_down_ms, result.bufferbloat_up_ms) if d is not None],
|
||||||
default=None,
|
default=None,
|
||||||
)
|
)
|
||||||
result.bufferbloat_grade = grade_bufferbloat(worst_delta, bands)
|
result.bufferbloat_grade = grade_bufferbloat(worst_delta, config.bands)
|
||||||
return result
|
return result
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ from netsleuth.models import SpeedResult
|
||||||
from netsleuth.speed import NDT7_CONSENT_NOTICE, run_speed_cascade
|
from netsleuth.speed import NDT7_CONSENT_NOTICE, run_speed_cascade
|
||||||
from netsleuth.speed import tier_ookla
|
from netsleuth.speed import tier_ookla
|
||||||
from netsleuth.config import BufferbloatBands
|
from netsleuth.config import BufferbloatBands
|
||||||
from netsleuth.speed import measure_with_bufferbloat, probe_while
|
from netsleuth.speed import BufferbloatMeasureConfig, measure_with_bufferbloat, probe_while
|
||||||
import httpx
|
import httpx
|
||||||
from pytest_httpx import HTTPXMock
|
from pytest_httpx import HTTPXMock
|
||||||
from netsleuth.config import Speedtest
|
from netsleuth.config import Speedtest
|
||||||
|
|
@ -371,12 +371,14 @@ async def test_measure_with_bufferbloat_fills_in_both_directions_and_the_grade()
|
||||||
|
|
||||||
result = await measure_with_bufferbloat(
|
result = await measure_with_bufferbloat(
|
||||||
SpeedResult(method="cloudflare", download_mbps=300.0, upload_mbps=40.0),
|
SpeedResult(method="cloudflare", download_mbps=300.0, upload_mbps=40.0),
|
||||||
|
config=BufferbloatMeasureConfig(
|
||||||
idle_rtt_ms=12.0,
|
idle_rtt_ms=12.0,
|
||||||
bands=BufferbloatBands(),
|
bands=BufferbloatBands(),
|
||||||
run_download=run_download,
|
run_download=run_download,
|
||||||
run_upload=run_upload,
|
run_upload=run_upload,
|
||||||
probe=probe,
|
probe=probe,
|
||||||
interval=0.05,
|
interval=0.05,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
assert result.idle_rtt_ms == 12.0
|
assert result.idle_rtt_ms == 12.0
|
||||||
assert result.loaded_rtt_down_ms is not None
|
assert result.loaded_rtt_down_ms is not None
|
||||||
|
|
@ -396,12 +398,14 @@ async def test_measure_with_bufferbloat_without_an_idle_baseline_grades_unknown(
|
||||||
|
|
||||||
result = await measure_with_bufferbloat(
|
result = await measure_with_bufferbloat(
|
||||||
SpeedResult(method="cloudflare", download_mbps=300.0),
|
SpeedResult(method="cloudflare", download_mbps=300.0),
|
||||||
|
config=BufferbloatMeasureConfig(
|
||||||
idle_rtt_ms=None,
|
idle_rtt_ms=None,
|
||||||
bands=BufferbloatBands(),
|
bands=BufferbloatBands(),
|
||||||
run_download=work,
|
run_download=work,
|
||||||
run_upload=work,
|
run_upload=work,
|
||||||
probe=probe,
|
probe=probe,
|
||||||
interval=0.02,
|
interval=0.02,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
assert result.bufferbloat_down_ms is None
|
assert result.bufferbloat_down_ms is None
|
||||||
assert result.bufferbloat_grade == "?"
|
assert result.bufferbloat_grade == "?"
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue