Skip to content
quarry Artifact [76402f67f7]
Login

Artifact [76402f67f7]

Artifact 76402f67f737facc30bcf2224fc85fa9dd2aef20d7cac158dd6ff8bfd4e900d8:


package markup

import (
	"fmt"
	"strings"
	"testing"
	"time"

	"golang.org/x/net/html/atom"
)

const atomH2 = atom.H2

// bigReadme is a plausible large README: sections of prose with inline
// markup and links, lists, a table, a fence and a quote, repeated to
// about 1 MB. Headings repeat, as they do in real documents.
func bigReadme(size int) []byte {
	var b strings.Builder
	for i := 0; b.Len() < size; i++ {
		// Real READMEs repeat headings ("Usage", "Example" per section);
		// each repeat needs its own anchor id.
		fmt.Fprintf(&b, "## Example\n\n")
		fmt.Fprintf(&b, "Some prose with **bold**, *italic*, `code %d`, a [link](https://example.com/%d), "+
			"an autolink https://example.org/p/%d and a reference to abcdef%04d0123.\n"+
			"A second sentence that runs on for a while so paragraphs have some weight to them.\n\n", i, i, i, i)
		fmt.Fprintf(&b, "- item %d one\n- item %d two\n  - nested %d\n- [x] done %d\n\n", i, i, i, i)
		fmt.Fprintf(&b, "| name | qty | note |\n|---|--:|---|\n| a%d | %d | first |\n| b%d | %d | second |\n\n", i, i, i, i*3)
		fmt.Fprintf(&b, "```go\nfunc f%d() int { return %d }\n```\n\n> quoted line %d\n\n", i, i, i)
		fmt.Fprintf(&b, "![img](https://example.com/i%d.png)\n\n", i)
	}
	return []byte(b.String())
}

func BenchmarkRenderLarge(b *testing.B) {
	src := bigReadme(1 << 20)
	b.SetBytes(int64(len(src)))
	for i := 0; i < b.N; i++ {
		if _, err := Render(src, fakeRes{}); err != nil {
			b.Fatal(err)
		}
	}
}

// A repository owner controls the README, and it renders on every page
// view: a megabyte must not cost seconds. The budget is generous — the
// point is to catch a super-linear pass, not to time a machine: the
// quadratic heading-id loop this guards took minutes at this size.
const budget = 5 * time.Second * raceSlowdown

// headingDense is a megabyte of tiny sections under the same heading —
// the shape that makes anchor generation quadratic.
func headingDense(size int) []byte {
	return []byte(strings.Repeat("## Notes\n\ntext\n\n", size/len("## Notes\n\ntext\n\n")+1))
}

func TestRenderHeadingDenseStaysWithinBudget(t *testing.T) {
	src := headingDense(1 << 20)
	start := time.Now()
	if _, err := Render(src, nil); err != nil {
		t.Fatal(err)
	}
	if d := time.Since(start); d > budget {
		t.Fatalf("Render of %d bytes took %v, budget %v", len(src), d, budget)
	}
}

func TestRenderLargeStaysWithinBudget(t *testing.T) {
	src := bigReadme(1 << 20)
	start := time.Now()
	if _, err := Render(src, fakeRes{}); err != nil {
		t.Fatal(err)
	}
	if d := time.Since(start); d > budget {
		t.Fatalf("Render of %d bytes took %v, budget %v", len(src), d, budget)
	}
}

// Repeated headings get distinct anchors, numbered in order, and an
// explicit heading whose slug already looks like a numbered anchor
// is not handed the same one twice.
func TestRepeatedHeadingIDsAreDistinct(t *testing.T) {
	src := "## Usage\n\n## Usage\n\n## Usage-1\n\n## Usage\n\n## Usage\n"
	ids := map[string]bool{}
	// The renderer demotes every heading a level: ## arrives as h3.
	for _, h := range findAll(parseBody(t, src), atom.H3) {
		id := attr(h, "id")
		if id == "" || ids[id] {
			t.Fatalf("id %q empty or repeated", id)
		}
		ids[id] = true
	}
	for _, want := range []string{"usage", "usage-1", "usage-1-1", "usage-2", "usage-3"} {
		if !ids[want] {
			t.Errorf("missing id %s in %v", want, ids)
		}
	}
}