P5a review: outlines from named destinations; pool race; fast over-limit; revalidation under the limiter; Accept pdf
- pdftext/outline: resolve named destinations through the catalog /Dests and the /Names /Dests tree, resolve indirect /Dest, /A and /Title, and treat reader.Null (what Dict.Get returns for a missing key) as absent. Before this every LaTeX and Acrobat PDF had an empty outline. pdftest gains NamedDests; TestOutlineNamedDestinations. - api: pool() always goes through its Once (data race on concurrent first use); TestDefaultPoolConcurrentFirstUse. Revalidation of a stale cache entry counts against the caller/host limit. - pdftext/runner: output over MaxText kills the child at once instead of waiting for the deadline; TestRunnerTooMuchTextFailsFast. - fetch: Accept includes application/pdf;q=0.8. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
package pdftext_test
|
||||
|
||||
// Owner review (P5a): output over MaxText fails fast, not at the deadline. The child writes more
|
||||
// than a pipe buffer (64 KiB), so it blocks on stdout until killed; Run must not wait for it to
|
||||
// exit on its own before checking the size.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.wntrmute.dev/kyle/sift/internal/pdftest"
|
||||
"git.wntrmute.dev/kyle/sift/internal/pdftext"
|
||||
)
|
||||
|
||||
func TestRunnerTooMuchTextFailsFast(t *testing.T) {
|
||||
var pages []string
|
||||
for p := 1; p <= 80; p++ {
|
||||
pages = append(pages, pdftest.Lorem(p, 40)) // ~2,700 characters a page, ~200 KiB in all
|
||||
}
|
||||
r := runner()
|
||||
r.MaxText = 1000
|
||||
r.Deadline = 30 * time.Second
|
||||
start := time.Now()
|
||||
_, err := r.Run(context.Background(), pdftest.Build(pdftest.Doc{Pages: pages}))
|
||||
if !errors.Is(err, pdftext.ErrTooMuchText) {
|
||||
t.Fatalf("err %v, want ErrTooMuchText", err)
|
||||
}
|
||||
if d := time.Since(start); d > 5*time.Second {
|
||||
t.Errorf("ErrTooMuchText after %v; it waited for the deadline instead of killing the child", d)
|
||||
}
|
||||
}
|
||||
|
||||
// Owner review (P5a): LaTeX (hyperref) and Acrobat outlines use named destinations through
|
||||
// the /Names tree and indirect titles; the first run returned no outline for any arXiv paper
|
||||
// or the NIST PDF. reader's Dict.Get answers reader.Null{} for a missing key, which hid /A.
|
||||
func TestOutlineNamedDestinations(t *testing.T) {
|
||||
b := pdftest.Build(pdftest.Doc{
|
||||
Title: "Named",
|
||||
Pages: []string{"one", "two", "three"},
|
||||
Outline: []pdftest.Outline{{Title: "1 Intro", Page: 1, Level: 1}, {Title: "2 Method", Page: 3, Level: 1}},
|
||||
NamedDests: true,
|
||||
})
|
||||
var out bytes.Buffer
|
||||
if err := pdftext.Extract(b, 10, &out); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var l struct{ Meta pdftext.Meta }
|
||||
line, _ := out.ReadBytes('\n')
|
||||
if err := json.Unmarshal(line, &l); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := []pdftext.OutlineItem{{Title: "1 Intro", Page: 1, Level: 1}, {Title: "2 Method", Page: 3, Level: 1}}
|
||||
if len(l.Meta.Outline) != 2 || l.Meta.Outline[0] != want[0] || l.Meta.Outline[1] != want[1] {
|
||||
t.Errorf("outline = %+v, want %+v", l.Meta.Outline, want)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user