Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/workflows/codspeed.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: CodSpeed

on:
push:
branches:
- "main"
pull_request:
workflow_dispatch:

permissions:
contents: read
id-token: write

jobs:
benchmarks:
name: Run benchmarks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5

- name: Set up Python
run: uv python install 3.14

- name: Install dependencies
run: uv sync --dev

- name: Run benchmarks
uses: CodSpeedHQ/action@v4
env:
DJANGO_SECRET_KEY: "ci-benchmark-secret-key"
AWS_ACCESS_KEY_ID: "test"
AWS_SECRET_ACCESS_KEY: "test"
DATABASE_URL: "sqlite:///dev.db"
with:
mode: simulation
run: uv run pytest tests/test_benchmarks.py --codspeed
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[![CodSpeed](https://img.shields.io/endpoint?url=https://codspeed.io/badge.json)](https://codspeed.io/tLDP/tldpy?utm_source=badge)

# tldpy

The Linux Documentation Project website.
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ dependencies = [
"loguru>=0.7.3",
"pg8000>=1.31.5",
]

[dependency-groups]
dev = [
"pytest-codspeed>=4.4.0",
]
Empty file added tests/__init__.py
Empty file.
169 changes: 169 additions & 0 deletions tests/test_benchmarks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
"""Performance benchmarks for tldpy view utilities."""

import pytest
from unittest.mock import patch

import sys
import os

# Add the tldpy app directory so Django modules can be imported
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "tldpy"))

# Configure Django settings before importing any Django code
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tldp.settings")
os.environ.setdefault("DJANGO_SECRET_KEY", "benchmark-secret-key")
os.environ.setdefault("AWS_ACCESS_KEY_ID", "test")
os.environ.setdefault("AWS_SECRET_ACCESS_KEY", "test")
os.environ.setdefault("DATABASE_URL", "sqlite:///dev.db")

import django

django.setup()

from tldp.views import (
decode_content,
extract_breadcrumbs,
extract_title,
get_category_for_key,
render_search_results,
)

# ---------------------------------------------------------------------------
# Fixtures: realistic HTML payloads
# ---------------------------------------------------------------------------

SMALL_HTML = (
"<html><head><title>3-Button Mouse HOWTO</title></head>"
"<body><h1>3-Button Mouse HOWTO</h1><p>Short document.</p></body></html>"
)

LARGE_HTML = (
"<html><head><title>Bash Beginners Guide</title></head><body>"
"<h1>Bash Beginners Guide</h1>"
+ "".join(
f"<h2>Chapter {i}</h2><p>{'Lorem ipsum dolor sit amet. ' * 40}</p>"
for i in range(50)
)
+ "</body></html>"
)

HTML_NO_TITLE = (
"<html><head></head><body><h1>Untitled Document</h1>"
"<p>Content without a title tag.</p></body></html>"
)

LDPLIST_DATA = {
"HOWTO": [f"howto-{i}" for i in range(464)],
"Guides": [f"guide-{i}" for i in range(22)],
"FAQs": [f"faq-{i}" for i in range(4)],
}

SEARCH_RESULTS = [
{
"key": f"doc-{i}",
"title": f"Document Title {i}",
"url": f"/en/doc-{i}/",
"category": "HOWTO" if i % 3 == 0 else ("Guides" if i % 3 == 1 else "FAQs"),
}
for i in range(50)
]


# ---------------------------------------------------------------------------
# decode_content
# ---------------------------------------------------------------------------


def test_bench_decode_content_bytes(benchmark):
"""Benchmark decoding byte content to string."""
payload = LARGE_HTML.encode("latin-1")
benchmark(decode_content, payload)


def test_bench_decode_content_str(benchmark):
"""Benchmark passthrough when content is already a string."""
benchmark(decode_content, LARGE_HTML)


# ---------------------------------------------------------------------------
# extract_title
# ---------------------------------------------------------------------------


def test_bench_extract_title_small(benchmark):
"""Benchmark title extraction from a small HTML document."""
result = benchmark(extract_title, SMALL_HTML)
assert result == "3-Button Mouse HOWTO"


def test_bench_extract_title_large(benchmark):
"""Benchmark title extraction from a large HTML document."""
result = benchmark(extract_title, LARGE_HTML)
assert result == "Bash Beginners Guide"


def test_bench_extract_title_missing(benchmark):
"""Benchmark title extraction when no <title> tag is present."""
result = benchmark(extract_title, HTML_NO_TITLE)
assert result is None


# ---------------------------------------------------------------------------
# extract_breadcrumbs
# ---------------------------------------------------------------------------


def test_bench_extract_breadcrumbs_small(benchmark):
"""Benchmark breadcrumb extraction from a small HTML document."""
result = benchmark(extract_breadcrumbs, SMALL_HTML)
assert len(result) == 1
assert result[0]["name"] == "3-Button Mouse HOWTO"


def test_bench_extract_breadcrumbs_large(benchmark):
"""Benchmark breadcrumb extraction from a large HTML document."""
result = benchmark(extract_breadcrumbs, LARGE_HTML)
assert len(result) == 1


# ---------------------------------------------------------------------------
# get_category_for_key
# ---------------------------------------------------------------------------


@patch("tldp.views.get_ldplist", return_value=LDPLIST_DATA)
def test_bench_get_category_first_match(mock_ldp, benchmark):
"""Benchmark category lookup - key found early in the first category."""
result = benchmark(get_category_for_key, "en", "howto-0")
assert result == "HOWTO"


@patch("tldp.views.get_ldplist", return_value=LDPLIST_DATA)
def test_bench_get_category_last_match(mock_ldp, benchmark):
"""Benchmark category lookup - key found in the last category."""
result = benchmark(get_category_for_key, "en", "faq-3")
assert result == "FAQs"


@patch("tldp.views.get_ldplist", return_value=LDPLIST_DATA)
def test_bench_get_category_miss(mock_ldp, benchmark):
"""Benchmark category lookup - key not found in any category."""
result = benchmark(get_category_for_key, "en", "nonexistent-key")
assert result is None


# ---------------------------------------------------------------------------
# render_search_results
# ---------------------------------------------------------------------------


def test_bench_render_search_results_many(benchmark):
"""Benchmark rendering 50 search results into HTML."""
result = benchmark(render_search_results, "bash", SEARCH_RESULTS)
assert "bash" in result


def test_bench_render_search_results_empty(benchmark):
"""Benchmark rendering empty search results."""
result = benchmark(render_search_results, "nothing", [])
assert "No results found" in result
Loading
Loading