7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
549 lines
16 KiB
Python
549 lines
16 KiB
Python
"""
|
|
Tests for benchmarks/efficiency/speed_profiler.py
|
|
|
|
Tests cover:
|
|
- SpeedProfiler initialization
|
|
- Session start/stop
|
|
- Timer management (start, stop, context manager)
|
|
- Timing retrieval and summary
|
|
- time_function decorator
|
|
"""
|
|
|
|
import time
|
|
from unittest.mock import patch
|
|
|
|
|
|
class TestSpeedProfilerInit:
|
|
"""Tests for SpeedProfiler initialization."""
|
|
|
|
def test_init_creates_empty_timings(self):
|
|
"""Test that initialization creates empty timings dict."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
|
|
assert profiler.timings == {}
|
|
|
|
def test_init_creates_empty_current_timers(self):
|
|
"""Test that initialization creates empty current_timers dict."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
|
|
assert profiler.current_timers == {}
|
|
|
|
def test_init_total_start_time_is_none(self):
|
|
"""Test that total_start_time is None on init."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
|
|
assert profiler.total_start_time is None
|
|
|
|
def test_init_total_end_time_is_none(self):
|
|
"""Test that total_end_time is None on init."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
|
|
assert profiler.total_end_time is None
|
|
|
|
|
|
class TestSpeedProfilerStartStop:
|
|
"""Tests for session start/stop."""
|
|
|
|
def test_start_sets_total_start_time(self):
|
|
"""Test that start() sets total_start_time."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
before = time.time()
|
|
profiler.start()
|
|
after = time.time()
|
|
|
|
assert profiler.total_start_time is not None
|
|
assert before <= profiler.total_start_time <= after
|
|
|
|
def test_start_clears_timings(self):
|
|
"""Test that start() clears any existing timings."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
profiler.timings = {"old_timer": {"total": 1.0}}
|
|
profiler.current_timers = {"running": time.time()}
|
|
|
|
profiler.start()
|
|
|
|
assert profiler.timings == {}
|
|
assert profiler.current_timers == {}
|
|
|
|
def test_stop_sets_total_end_time(self):
|
|
"""Test that stop() sets total_end_time."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
profiler.start()
|
|
before = time.time()
|
|
profiler.stop()
|
|
after = time.time()
|
|
|
|
assert profiler.total_end_time is not None
|
|
assert before <= profiler.total_end_time <= after
|
|
|
|
def test_stop_stops_running_timers(self):
|
|
"""Test that stop() stops any running timers."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
profiler.start()
|
|
profiler.start_timer("running_timer")
|
|
|
|
assert "running_timer" in profiler.current_timers
|
|
|
|
profiler.stop()
|
|
|
|
assert "running_timer" not in profiler.current_timers
|
|
assert "running_timer" in profiler.timings
|
|
|
|
def test_stop_records_total_duration(self):
|
|
"""Test that stop() allows calculating total duration."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
profiler.start()
|
|
time.sleep(0.01) # Small delay
|
|
profiler.stop()
|
|
|
|
duration = profiler.total_end_time - profiler.total_start_time
|
|
assert duration >= 0.01
|
|
|
|
|
|
class TestSpeedProfilerTimers:
|
|
"""Tests for timer management."""
|
|
|
|
def test_start_timer_adds_to_current_timers(self):
|
|
"""Test that start_timer adds timer to current_timers."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
before = time.time()
|
|
profiler.start_timer("my_timer")
|
|
after = time.time()
|
|
|
|
assert "my_timer" in profiler.current_timers
|
|
assert before <= profiler.current_timers["my_timer"] <= after
|
|
|
|
def test_start_timer_restarts_existing_timer(self):
|
|
"""Test that starting an existing timer restarts it."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
profiler.start_timer("my_timer")
|
|
old_time = profiler.current_timers["my_timer"]
|
|
|
|
time.sleep(0.01)
|
|
profiler.start_timer("my_timer")
|
|
new_time = profiler.current_timers["my_timer"]
|
|
|
|
assert new_time > old_time
|
|
|
|
def test_stop_timer_removes_from_current_timers(self):
|
|
"""Test that stop_timer removes from current_timers."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
profiler.start_timer("my_timer")
|
|
profiler.stop_timer("my_timer")
|
|
|
|
assert "my_timer" not in profiler.current_timers
|
|
|
|
def test_stop_timer_adds_to_timings(self):
|
|
"""Test that stop_timer adds timing data."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
profiler.start_timer("my_timer")
|
|
time.sleep(0.01)
|
|
profiler.stop_timer("my_timer")
|
|
|
|
assert "my_timer" in profiler.timings
|
|
assert profiler.timings["my_timer"]["count"] == 1
|
|
assert profiler.timings["my_timer"]["total"] >= 0.01
|
|
|
|
def test_stop_timer_accumulates_count(self):
|
|
"""Test that multiple timer runs accumulate count."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
|
|
for _ in range(3):
|
|
profiler.start_timer("my_timer")
|
|
profiler.stop_timer("my_timer")
|
|
|
|
assert profiler.timings["my_timer"]["count"] == 3
|
|
|
|
def test_stop_timer_tracks_min_max(self):
|
|
"""Test that timer tracks min and max values."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
|
|
# First run
|
|
profiler.start_timer("my_timer")
|
|
time.sleep(0.01)
|
|
profiler.stop_timer("my_timer")
|
|
|
|
# Second run
|
|
profiler.start_timer("my_timer")
|
|
time.sleep(0.02)
|
|
profiler.stop_timer("my_timer")
|
|
|
|
assert (
|
|
profiler.timings["my_timer"]["min"]
|
|
< profiler.timings["my_timer"]["max"]
|
|
)
|
|
|
|
def test_stop_timer_not_started_does_nothing(self):
|
|
"""Test that stopping a non-existent timer does nothing."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
profiler.stop_timer("nonexistent")
|
|
|
|
assert "nonexistent" not in profiler.timings
|
|
|
|
def test_timer_context_manager_starts_and_stops(self):
|
|
"""Test timer context manager starts and stops timer."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
|
|
with profiler.timer("context_timer"):
|
|
assert "context_timer" in profiler.current_timers
|
|
|
|
assert "context_timer" not in profiler.current_timers
|
|
assert "context_timer" in profiler.timings
|
|
|
|
def test_timer_context_manager_records_time(self):
|
|
"""Test timer context manager records elapsed time."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
|
|
with profiler.timer("context_timer"):
|
|
time.sleep(0.01)
|
|
|
|
assert profiler.timings["context_timer"]["total"] >= 0.01
|
|
|
|
|
|
class TestSpeedProfilerGetTimings:
|
|
"""Tests for timing retrieval."""
|
|
|
|
def test_get_timings_returns_copy(self):
|
|
"""Test that get_timings returns a copy of timings."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
profiler.start_timer("test")
|
|
profiler.stop_timer("test")
|
|
|
|
result = profiler.get_timings()
|
|
result["modified"] = True
|
|
|
|
assert "modified" not in profiler.timings
|
|
|
|
def test_get_timings_calculates_averages(self):
|
|
"""Test that get_timings calculates averages."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
|
|
for _ in range(4):
|
|
profiler.start_timer("test")
|
|
profiler.stop_timer("test")
|
|
|
|
result = profiler.get_timings()
|
|
|
|
assert "avg" in result["test"]
|
|
expected_avg = result["test"]["total"] / result["test"]["count"]
|
|
assert abs(result["test"]["avg"] - expected_avg) < 0.0001
|
|
|
|
def test_get_timings_includes_total_duration(self):
|
|
"""Test that get_timings includes total session duration."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
profiler.start()
|
|
time.sleep(0.01)
|
|
profiler.stop()
|
|
|
|
result = profiler.get_timings()
|
|
|
|
assert "total" in result
|
|
assert result["total"]["total"] >= 0.01
|
|
|
|
def test_get_timings_empty_when_no_timers(self):
|
|
"""Test that get_timings returns empty dict when no timers used."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
|
|
result = profiler.get_timings()
|
|
|
|
assert result == {}
|
|
|
|
|
|
class TestSpeedProfilerGetSummary:
|
|
"""Tests for summary generation."""
|
|
|
|
def test_get_summary_includes_total_duration(self):
|
|
"""Test that summary includes total_duration."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
profiler.start()
|
|
time.sleep(0.01)
|
|
profiler.stop()
|
|
|
|
summary = profiler.get_summary()
|
|
|
|
assert "total_duration" in summary
|
|
assert summary["total_duration"] >= 0.01
|
|
|
|
def test_get_summary_calculates_percentages(self):
|
|
"""Test that summary calculates component percentages."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
profiler.start()
|
|
|
|
with profiler.timer("component"):
|
|
time.sleep(0.01)
|
|
|
|
profiler.stop()
|
|
|
|
summary = profiler.get_summary()
|
|
|
|
assert "component_percent" in summary
|
|
assert 0 <= summary["component_percent"] <= 100
|
|
|
|
def test_get_summary_includes_per_operation_times(self):
|
|
"""Test that summary includes per-operation times."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
|
|
for _ in range(3):
|
|
profiler.start_timer("op")
|
|
profiler.stop_timer("op")
|
|
|
|
summary = profiler.get_summary()
|
|
|
|
assert "op_per_operation" in summary
|
|
|
|
def test_get_summary_handles_zero_duration(self):
|
|
"""Test that summary handles zero total duration gracefully."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
profiler.start_timer("test")
|
|
profiler.stop_timer("test")
|
|
|
|
# Don't call start/stop, so total_duration comes from sum of timers
|
|
summary = profiler.get_summary()
|
|
|
|
# Should not raise division by zero
|
|
assert "total_duration" in summary
|
|
|
|
|
|
class TestSpeedProfilerPrintSummary:
|
|
"""Tests for print_summary."""
|
|
|
|
def test_print_summary_outputs_header(self, capsys):
|
|
"""Test that print_summary outputs header."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
profiler.start()
|
|
profiler.stop()
|
|
|
|
profiler.print_summary()
|
|
|
|
captured = capsys.readouterr()
|
|
assert "SPEED PROFILE SUMMARY" in captured.out
|
|
|
|
def test_print_summary_shows_total_time(self, capsys):
|
|
"""Test that print_summary shows total execution time."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
profiler.start()
|
|
time.sleep(0.01)
|
|
profiler.stop()
|
|
|
|
profiler.print_summary()
|
|
|
|
captured = capsys.readouterr()
|
|
assert "Total execution time:" in captured.out
|
|
|
|
|
|
class TestTimeFunctionDecorator:
|
|
"""Tests for time_function decorator."""
|
|
|
|
def test_time_function_returns_result(self):
|
|
"""Test that decorated function returns correct result."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
time_function,
|
|
)
|
|
|
|
@time_function
|
|
def add(a, b):
|
|
return a + b
|
|
|
|
result = add(2, 3)
|
|
|
|
assert result == 5
|
|
|
|
def test_time_function_logs_execution_time(self):
|
|
"""Test that decorator logs execution time."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
time_function,
|
|
)
|
|
|
|
@time_function
|
|
def slow_function():
|
|
time.sleep(0.01)
|
|
return "done"
|
|
|
|
with patch(
|
|
"local_deep_research.benchmarks.efficiency.speed_profiler.logger"
|
|
) as mock_logger:
|
|
slow_function()
|
|
|
|
mock_logger.info.assert_called_once()
|
|
call_args = mock_logger.info.call_args[0][0]
|
|
assert "slow_function" in call_args
|
|
assert "seconds" in call_args
|
|
|
|
def test_time_function_preserves_args_kwargs(self):
|
|
"""Test that decorator preserves function arguments."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
time_function,
|
|
)
|
|
|
|
@time_function
|
|
def greet(name, greeting="Hello"):
|
|
return f"{greeting}, {name}!"
|
|
|
|
result = greet("World", greeting="Hi")
|
|
|
|
assert result == "Hi, World!"
|
|
|
|
|
|
class TestSpeedProfilerTimingData:
|
|
"""Tests for timing data structure."""
|
|
|
|
def test_timing_data_includes_starts_list(self):
|
|
"""Test that timing data includes list of start times."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
|
|
for _ in range(3):
|
|
profiler.start_timer("test")
|
|
profiler.stop_timer("test")
|
|
|
|
assert "starts" in profiler.timings["test"]
|
|
assert len(profiler.timings["test"]["starts"]) == 3
|
|
|
|
def test_timing_data_includes_durations_list(self):
|
|
"""Test that timing data includes list of durations."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
|
|
for _ in range(3):
|
|
profiler.start_timer("test")
|
|
profiler.stop_timer("test")
|
|
|
|
assert "durations" in profiler.timings["test"]
|
|
assert len(profiler.timings["test"]["durations"]) == 3
|
|
|
|
def test_timing_data_total_equals_sum_of_durations(self):
|
|
"""Test that total equals sum of individual durations."""
|
|
from local_deep_research.benchmarks.efficiency.speed_profiler import (
|
|
SpeedProfiler,
|
|
)
|
|
|
|
profiler = SpeedProfiler()
|
|
|
|
for _ in range(3):
|
|
profiler.start_timer("test")
|
|
time.sleep(0.01)
|
|
profiler.stop_timer("test")
|
|
|
|
total = profiler.timings["test"]["total"]
|
|
sum_durations = sum(profiler.timings["test"]["durations"])
|
|
|
|
assert abs(total - sum_durations) < 0.0001
|