12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364 | package render
import (
"bytes"
"encoding/xml"
"errors"
"fmt"
"io"
"slices"
"strings"
"testing"
)
// svgTokens is how svgRootEnd's decoder reads an SVG: every token up to
// the first error, and whether there was one besides the end of input.
func svgTokens(data []byte) (toks []string, broken bool) {
d := xml.NewDecoder(bytes.NewReader(data))
d.Strict = true
d.Entity = xml.HTMLEntity
for {
tok, err := d.RawToken()
if err != nil {
return toks, !errors.Is(err, io.EOF)
}
toks = append(toks, fmt.Sprintf("%#v", xml.CopyToken(tok)))
}
}
// Pushed SVGs are themed by splicing a <style> in after the root start tag.
// Whatever the file holds, the splice must land between tags and leave the
// rest of the document reading exactly as before.
func FuzzThemeSVG(f *testing.F) {
for _, s := range []string{
`<svg><rect/></svg>`, `<?xml version="1.0"?><!-- c --><!DOCTYPE svg><svg a="1"><g/></svg>`,
`<svg title="a>b" x='/'><g/></svg>`, `<svg a="1"/>`, `<html><svg/></html>`, `<svg a=1>`,
`<svg><![CDATA[</svg>]]></svg>`, `<SVG xmlns="http://www.w3.org/2000/svg"> </SVG>`, `<s:svg>`, "",
} {
f.Add([]byte(s))
}
f.Fuzz(func(t *testing.T, data []byte) {
end := svgRootEnd(data)
themed := themeSVG(data, end, "#fff", "#000")
if end < 0 {
if !bytes.Equal(themed, data) {
t.Fatalf("%q changed with no root found", data)
}
return
}
if end < 2 || data[end-1] != '>' || data[end-2] == '/' {
t.Fatalf("%q: root ends at %d", data, end)
}
want, wantBroken := svgTokens(data)
got, gotBroken := svgTokens(themed)
if len(got) != len(want)+3 || gotBroken != wantBroken {
t.Fatalf("%q themed as %q reads differently", data, themed)
}
// The root start tag, then the injected <style>, its text and </style>.
root := slices.IndexFunc(want, func(s string) bool { return strings.HasPrefix(s, "xml.StartElement") })
spliced := slices.Concat(got[:root+1], got[root+4:])
if !slices.Equal(spliced, want) || !strings.Contains(got[root+1], `Local:"style"`) {
t.Fatalf("%q themed as %q reads differently:\n%v\n%v", data, themed, want, got)
}
})
}
|