encoding/xml: improve the test coverage, fix minor bugs

Improve the test coverage of encoding/xml package by adding
the test cases for the execution paths that were not covered before.

Since it reveals a couple of issues, fix them as well while we're at it.

As I used an `strings.EqualFold` instead of adding one more `strings.ToLower`,
our fix to `autoClose()` tends to run faster as well as a result.

	name             old time/op    new time/op    delta
	HTMLAutoClose-8    5.93µs ± 2%    5.75µs ± 3%  -3.16%  (p=0.000 n=10+10)
	name             old alloc/op   new alloc/op   delta
	HTMLAutoClose-8    2.60kB ± 0%    2.58kB ± 0%  -0.46%  (p=0.000 n=10+10)
	name             old allocs/op  new allocs/op  delta
	HTMLAutoClose-8      72.0 ± 0%      67.0 ± 0%  -6.94%  (p=0.000 n=10+10)

The overall `encoding/xml` test coverage increase is `88.1% -> 89.9%`;
although it may look insignificant, this CL covers some important corner cases,
like `autoClose()` functionality (that was not tested at all).

Fixes #49635
Fixes #49636

Change-Id: I50b2769896c197eb285672313b7148f4fe8bdb38
Reviewed-on: https://go-review.googlesource.com/c/go/+/364734
Trust: Bryan Mills <bcmills@google.com>
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
Trust: Daniel Martí <mvdan@mvdan.cc>
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
Iskander Sharipov 2021-11-17 17:46:22 +03:00 committed by Daniel Martí
parent 2b8aa2b734
commit e79c39f004
2 changed files with 168 additions and 7 deletions

View file

@ -673,6 +673,19 @@ func TestCopyTokenStartElement(t *testing.T) {
}
}
func TestCopyTokenComment(t *testing.T) {
data := []byte("<!-- some comment -->")
var tok1 Token = Comment(data)
tok2 := CopyToken(tok1)
if !reflect.DeepEqual(tok1, tok2) {
t.Error("CopyToken(Comment) != Comment")
}
data[1] = 'o'
if reflect.DeepEqual(tok1, tok2) {
t.Error("CopyToken(Comment) uses same buffer.")
}
}
func TestSyntaxErrorLineNum(t *testing.T) {
testInput := "<P>Foo<P>\n\n<P>Bar</>\n"
d := NewDecoder(strings.NewReader(testInput))
@ -1060,3 +1073,155 @@ func TestRoundTrip(t *testing.T) {
t.Run(name, func(t *testing.T) { testRoundTrip(t, input) })
}
}
func TestParseErrors(t *testing.T) {
withDefaultHeader := func(s string) string {
return `<?xml version="1.0" encoding="UTF-8"?>` + s
}
tests := []struct {
src string
err string
}{
{withDefaultHeader(`</foo>`), `unexpected end element </foo>`},
{withDefaultHeader(`<x:foo></y:foo>`), `element <foo> in space x closed by </foo> in space y`},
{withDefaultHeader(`<? not ok ?>`), `expected target name after <?`},
{withDefaultHeader(`<!- not ok -->`), `invalid sequence <!- not part of <!--`},
{withDefaultHeader(`<!-? not ok -->`), `invalid sequence <!- not part of <!--`},
{withDefaultHeader(`<![not ok]>`), `invalid <![ sequence`},
{withDefaultHeader("\xf1"), `invalid UTF-8`},
// Header-related errors.
{`<?xml version="1.1" encoding="UTF-8"?>`, `unsupported version "1.1"; only version 1.0 is supported`},
// Cases below are for "no errors".
{withDefaultHeader(`<?ok?>`), ``},
{withDefaultHeader(`<?ok version="ok"?>`), ``},
}
for _, test := range tests {
d := NewDecoder(strings.NewReader(test.src))
var err error
for {
_, err = d.Token()
if err != nil {
break
}
}
if test.err == "" {
if err != io.EOF {
t.Errorf("parse %s: have %q error, expected none", test.src, err)
}
continue
}
if err == nil || err == io.EOF {
t.Errorf("parse %s: have no error, expected a non-nil error", test.src)
continue
}
if !strings.Contains(err.Error(), test.err) {
t.Errorf("parse %s: can't find %q error sudbstring\nerror: %q", test.src, test.err, err)
continue
}
}
}
const testInputHTMLAutoClose = `<?xml version="1.0" encoding="UTF-8"?>
<br>
<br/><br/>
<br><br>
<br></br>
<BR>
<BR/><BR/>
<Br></Br>
<BR><span id="test">abc</span><br/><br/>`
func BenchmarkHTMLAutoClose(b *testing.B) {
b.RunParallel(func(p *testing.PB) {
for p.Next() {
d := NewDecoder(strings.NewReader(testInputHTMLAutoClose))
d.Strict = false
d.AutoClose = HTMLAutoClose
d.Entity = HTMLEntity
for {
_, err := d.Token()
if err != nil {
if err == io.EOF {
break
}
b.Fatalf("unexpected error: %v", err)
}
}
}
})
}
func TestHTMLAutoClose(t *testing.T) {
wantTokens := []Token{
ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)},
CharData("\n"),
StartElement{Name{"", "br"}, []Attr{}},
EndElement{Name{"", "br"}},
CharData("\n"),
StartElement{Name{"", "br"}, []Attr{}},
EndElement{Name{"", "br"}},
StartElement{Name{"", "br"}, []Attr{}},
EndElement{Name{"", "br"}},
CharData("\n"),
StartElement{Name{"", "br"}, []Attr{}},
EndElement{Name{"", "br"}},
StartElement{Name{"", "br"}, []Attr{}},
EndElement{Name{"", "br"}},
CharData("\n"),
StartElement{Name{"", "br"}, []Attr{}},
EndElement{Name{"", "br"}},
CharData("\n"),
StartElement{Name{"", "BR"}, []Attr{}},
EndElement{Name{"", "BR"}},
CharData("\n"),
StartElement{Name{"", "BR"}, []Attr{}},
EndElement{Name{"", "BR"}},
StartElement{Name{"", "BR"}, []Attr{}},
EndElement{Name{"", "BR"}},
CharData("\n"),
StartElement{Name{"", "Br"}, []Attr{}},
EndElement{Name{"", "Br"}},
CharData("\n"),
StartElement{Name{"", "BR"}, []Attr{}},
EndElement{Name{"", "BR"}},
StartElement{Name{"", "span"}, []Attr{{Name: Name{"", "id"}, Value: "test"}}},
CharData("abc"),
EndElement{Name{"", "span"}},
StartElement{Name{"", "br"}, []Attr{}},
EndElement{Name{"", "br"}},
StartElement{Name{"", "br"}, []Attr{}},
EndElement{Name{"", "br"}},
}
d := NewDecoder(strings.NewReader(testInputHTMLAutoClose))
d.Strict = false
d.AutoClose = HTMLAutoClose
d.Entity = HTMLEntity
var haveTokens []Token
for {
tok, err := d.Token()
if err != nil {
if err == io.EOF {
break
}
t.Fatalf("unexpected error: %v", err)
}
haveTokens = append(haveTokens, CopyToken(tok))
}
if len(haveTokens) != len(wantTokens) {
t.Errorf("tokens count mismatch: have %d, want %d", len(haveTokens), len(wantTokens))
}
for i, want := range wantTokens {
if i >= len(haveTokens) {
t.Errorf("token[%d] expected %#v, have no token", i, want)
} else {
have := haveTokens[i]
if !reflect.DeepEqual(have, want) {
t.Errorf("token[%d] mismatch:\nhave: %#v\nwant: %#v", i, have, want)
}
}
}
}