2009-10-05 15:00:50 -07:00
|
|
|
// Copyright 2009 The Go Authors. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
package xml
|
|
|
|
|
|
|
|
|
|
import (
|
2012-05-17 00:04:00 -03:00
|
|
|
"fmt"
|
2009-12-15 15:41:46 -08:00
|
|
|
"io"
|
|
|
|
|
"reflect"
|
2011-04-21 14:37:26 -07:00
|
|
|
"strings"
|
2009-12-15 15:41:46 -08:00
|
|
|
"testing"
|
2009-10-05 15:00:50 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const testInput = `
|
|
|
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
|
|
|
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
2009-12-09 16:54:07 -08:00
|
|
|
<body xmlns:foo="ns1" xmlns="ns2" xmlns:tag="ns3" ` +
|
|
|
|
|
"\r\n\t" + ` >
|
2009-10-05 15:00:50 -07:00
|
|
|
<hello lang="en">World <>'" 白鵬翔</hello>
|
2012-10-21 20:33:24 -04:00
|
|
|
<query>&何; &is-it;</query>
|
2009-10-05 15:00:50 -07:00
|
|
|
<goodbye />
|
|
|
|
|
<outer foo:attr="value" xmlns:tag="ns4">
|
|
|
|
|
<inner/>
|
|
|
|
|
</outer>
|
|
|
|
|
<tag:name>
|
2009-11-14 11:46:09 -08:00
|
|
|
<![CDATA[Some text here.]]>
|
2009-10-05 15:00:50 -07:00
|
|
|
</tag:name>
|
|
|
|
|
</body><!-- missing final newline -->`
|
|
|
|
|
|
2012-10-21 20:33:24 -04:00
|
|
|
var testEntity = map[string]string{"何": "What", "is-it": "is it?"}
|
|
|
|
|
|
2009-10-05 15:00:50 -07:00
|
|
|
var rawTokens = []Token{
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n"),
|
2010-02-25 16:01:29 -08:00
|
|
|
ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)},
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n"),
|
|
|
|
|
Directive(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
2010-02-25 16:01:29 -08:00
|
|
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"`),
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n"),
|
2010-10-22 10:06:33 -07:00
|
|
|
StartElement{Name{"", "body"}, []Attr{{Name{"xmlns", "foo"}, "ns1"}, {Name{"", "xmlns"}, "ns2"}, {Name{"xmlns", "tag"}, "ns3"}}},
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n "),
|
2010-10-22 10:06:33 -07:00
|
|
|
StartElement{Name{"", "hello"}, []Attr{{Name{"", "lang"}, "en"}}},
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("World <>'\" 白鵬翔"),
|
2009-10-05 15:00:50 -07:00
|
|
|
EndElement{Name{"", "hello"}},
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n "),
|
2012-10-21 20:33:24 -04:00
|
|
|
StartElement{Name{"", "query"}, []Attr{}},
|
|
|
|
|
CharData("What is it?"),
|
|
|
|
|
EndElement{Name{"", "query"}},
|
|
|
|
|
CharData("\n "),
|
2011-11-15 10:28:01 +09:00
|
|
|
StartElement{Name{"", "goodbye"}, []Attr{}},
|
2009-10-05 15:00:50 -07:00
|
|
|
EndElement{Name{"", "goodbye"}},
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n "),
|
2010-10-22 10:06:33 -07:00
|
|
|
StartElement{Name{"", "outer"}, []Attr{{Name{"foo", "attr"}, "value"}, {Name{"xmlns", "tag"}, "ns4"}}},
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n "),
|
2011-11-15 10:28:01 +09:00
|
|
|
StartElement{Name{"", "inner"}, []Attr{}},
|
2009-10-05 15:00:50 -07:00
|
|
|
EndElement{Name{"", "inner"}},
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n "),
|
2009-10-05 15:00:50 -07:00
|
|
|
EndElement{Name{"", "outer"}},
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n "),
|
2011-11-15 10:28:01 +09:00
|
|
|
StartElement{Name{"tag", "name"}, []Attr{}},
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n "),
|
|
|
|
|
CharData("Some text here."),
|
|
|
|
|
CharData("\n "),
|
2009-10-05 15:00:50 -07:00
|
|
|
EndElement{Name{"tag", "name"}},
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n"),
|
2009-10-05 15:00:50 -07:00
|
|
|
EndElement{Name{"", "body"}},
|
2011-11-22 12:30:02 -05:00
|
|
|
Comment(" missing final newline "),
|
2009-10-05 15:00:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var cookedTokens = []Token{
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n"),
|
2010-02-25 16:01:29 -08:00
|
|
|
ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)},
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n"),
|
|
|
|
|
Directive(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
2010-02-25 16:01:29 -08:00
|
|
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"`),
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n"),
|
2010-10-22 10:06:33 -07:00
|
|
|
StartElement{Name{"ns2", "body"}, []Attr{{Name{"xmlns", "foo"}, "ns1"}, {Name{"", "xmlns"}, "ns2"}, {Name{"xmlns", "tag"}, "ns3"}}},
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n "),
|
2010-10-22 10:06:33 -07:00
|
|
|
StartElement{Name{"ns2", "hello"}, []Attr{{Name{"", "lang"}, "en"}}},
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("World <>'\" 白鵬翔"),
|
2009-10-05 15:00:50 -07:00
|
|
|
EndElement{Name{"ns2", "hello"}},
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n "),
|
2012-10-21 20:33:24 -04:00
|
|
|
StartElement{Name{"ns2", "query"}, []Attr{}},
|
|
|
|
|
CharData("What is it?"),
|
|
|
|
|
EndElement{Name{"ns2", "query"}},
|
|
|
|
|
CharData("\n "),
|
2011-11-15 10:28:01 +09:00
|
|
|
StartElement{Name{"ns2", "goodbye"}, []Attr{}},
|
2009-10-05 15:00:50 -07:00
|
|
|
EndElement{Name{"ns2", "goodbye"}},
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n "),
|
2010-10-22 10:06:33 -07:00
|
|
|
StartElement{Name{"ns2", "outer"}, []Attr{{Name{"ns1", "attr"}, "value"}, {Name{"xmlns", "tag"}, "ns4"}}},
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n "),
|
2011-11-15 10:28:01 +09:00
|
|
|
StartElement{Name{"ns2", "inner"}, []Attr{}},
|
2009-10-05 15:00:50 -07:00
|
|
|
EndElement{Name{"ns2", "inner"}},
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n "),
|
2009-10-05 15:00:50 -07:00
|
|
|
EndElement{Name{"ns2", "outer"}},
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n "),
|
2011-11-15 10:28:01 +09:00
|
|
|
StartElement{Name{"ns3", "name"}, []Attr{}},
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n "),
|
|
|
|
|
CharData("Some text here."),
|
|
|
|
|
CharData("\n "),
|
2009-10-05 15:00:50 -07:00
|
|
|
EndElement{Name{"ns3", "name"}},
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n"),
|
2009-10-05 15:00:50 -07:00
|
|
|
EndElement{Name{"ns2", "body"}},
|
2011-11-22 12:30:02 -05:00
|
|
|
Comment(" missing final newline "),
|
2009-10-05 15:00:50 -07:00
|
|
|
}
|
|
|
|
|
|
2011-04-21 14:37:26 -07:00
|
|
|
const testInputAltEncoding = `
|
|
|
|
|
<?xml version="1.0" encoding="x-testing-uppercase"?>
|
|
|
|
|
<TAG>VALUE</TAG>`
|
|
|
|
|
|
|
|
|
|
var rawTokensAltEncoding = []Token{
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n"),
|
2011-04-21 14:37:26 -07:00
|
|
|
ProcInst{"xml", []byte(`version="1.0" encoding="x-testing-uppercase"`)},
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n"),
|
2011-11-15 10:28:01 +09:00
|
|
|
StartElement{Name{"", "tag"}, []Attr{}},
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("value"),
|
2011-04-21 14:37:26 -07:00
|
|
|
EndElement{Name{"", "tag"}},
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-14 19:28:36 -08:00
|
|
|
var xmlInput = []string{
|
|
|
|
|
// unexpected EOF cases
|
|
|
|
|
"<",
|
|
|
|
|
"<t",
|
|
|
|
|
"<t ",
|
|
|
|
|
"<t/",
|
|
|
|
|
"<!",
|
|
|
|
|
"<!-",
|
|
|
|
|
"<!--",
|
|
|
|
|
"<!--c-",
|
|
|
|
|
"<!--c--",
|
|
|
|
|
"<!d",
|
|
|
|
|
"<t></",
|
|
|
|
|
"<t></t",
|
|
|
|
|
"<?",
|
|
|
|
|
"<?p",
|
|
|
|
|
"<t a",
|
|
|
|
|
"<t a=",
|
|
|
|
|
"<t a='",
|
|
|
|
|
"<t a=''",
|
|
|
|
|
"<t/><![",
|
|
|
|
|
"<t/><![C",
|
|
|
|
|
"<t/><![CDATA[d",
|
|
|
|
|
"<t/><![CDATA[d]",
|
|
|
|
|
"<t/><![CDATA[d]]",
|
|
|
|
|
|
|
|
|
|
// other Syntax errors
|
|
|
|
|
"<>",
|
|
|
|
|
"<t/a",
|
|
|
|
|
"<0 />",
|
|
|
|
|
"<?0 >",
|
|
|
|
|
// "<!0 >", // let the Token() caller handle
|
|
|
|
|
"</0>",
|
|
|
|
|
"<t 0=''>",
|
|
|
|
|
"<t a='&'>",
|
|
|
|
|
"<t a='<'>",
|
|
|
|
|
"<t> c;</t>",
|
|
|
|
|
"<t a>",
|
|
|
|
|
"<t a=>",
|
|
|
|
|
"<t a=v>",
|
|
|
|
|
// "<![CDATA[d]]>", // let the Token() caller handle
|
|
|
|
|
"<t></e>",
|
|
|
|
|
"<t></>",
|
|
|
|
|
"<t></t!",
|
|
|
|
|
"<t>cdata]]></t>",
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-05 15:00:50 -07:00
|
|
|
func TestRawToken(t *testing.T) {
|
2012-01-24 01:10:32 -02:00
|
|
|
d := NewDecoder(strings.NewReader(testInput))
|
2012-10-21 20:33:24 -04:00
|
|
|
d.Entity = testEntity
|
2012-01-24 01:10:32 -02:00
|
|
|
testRawToken(t, d, rawTokens)
|
2011-04-21 14:37:26 -07:00
|
|
|
}
|
|
|
|
|
|
2012-05-17 00:04:00 -03:00
|
|
|
const nonStrictInput = `
|
|
|
|
|
<tag>non&entity</tag>
|
|
|
|
|
<tag>&unknown;entity</tag>
|
|
|
|
|
<tag>{</tag>
|
|
|
|
|
<tag>&#zzz;</tag>
|
2012-10-21 20:33:24 -04:00
|
|
|
<tag>&なまえ3;</tag>
|
|
|
|
|
<tag><-gt;</tag>
|
|
|
|
|
<tag>&;</tag>
|
|
|
|
|
<tag>&0a;</tag>
|
2012-05-17 00:04:00 -03:00
|
|
|
`
|
|
|
|
|
|
2012-10-21 20:33:24 -04:00
|
|
|
var nonStringEntity = map[string]string{"": "oops!", "0a": "oops!"}
|
|
|
|
|
|
2012-05-17 00:04:00 -03:00
|
|
|
var nonStrictTokens = []Token{
|
|
|
|
|
CharData("\n"),
|
|
|
|
|
StartElement{Name{"", "tag"}, []Attr{}},
|
|
|
|
|
CharData("non&entity"),
|
|
|
|
|
EndElement{Name{"", "tag"}},
|
|
|
|
|
CharData("\n"),
|
|
|
|
|
StartElement{Name{"", "tag"}, []Attr{}},
|
|
|
|
|
CharData("&unknown;entity"),
|
|
|
|
|
EndElement{Name{"", "tag"}},
|
|
|
|
|
CharData("\n"),
|
|
|
|
|
StartElement{Name{"", "tag"}, []Attr{}},
|
|
|
|
|
CharData("{"),
|
|
|
|
|
EndElement{Name{"", "tag"}},
|
|
|
|
|
CharData("\n"),
|
|
|
|
|
StartElement{Name{"", "tag"}, []Attr{}},
|
|
|
|
|
CharData("&#zzz;"),
|
|
|
|
|
EndElement{Name{"", "tag"}},
|
|
|
|
|
CharData("\n"),
|
2012-10-21 20:33:24 -04:00
|
|
|
StartElement{Name{"", "tag"}, []Attr{}},
|
|
|
|
|
CharData("&なまえ3;"),
|
|
|
|
|
EndElement{Name{"", "tag"}},
|
|
|
|
|
CharData("\n"),
|
|
|
|
|
StartElement{Name{"", "tag"}, []Attr{}},
|
|
|
|
|
CharData("<-gt;"),
|
|
|
|
|
EndElement{Name{"", "tag"}},
|
|
|
|
|
CharData("\n"),
|
|
|
|
|
StartElement{Name{"", "tag"}, []Attr{}},
|
|
|
|
|
CharData("&;"),
|
|
|
|
|
EndElement{Name{"", "tag"}},
|
|
|
|
|
CharData("\n"),
|
|
|
|
|
StartElement{Name{"", "tag"}, []Attr{}},
|
|
|
|
|
CharData("&0a;"),
|
|
|
|
|
EndElement{Name{"", "tag"}},
|
|
|
|
|
CharData("\n"),
|
2012-05-17 00:04:00 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestNonStrictRawToken(t *testing.T) {
|
|
|
|
|
d := NewDecoder(strings.NewReader(nonStrictInput))
|
|
|
|
|
d.Strict = false
|
|
|
|
|
testRawToken(t, d, nonStrictTokens)
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-21 14:37:26 -07:00
|
|
|
type downCaser struct {
|
|
|
|
|
t *testing.T
|
|
|
|
|
r io.ByteReader
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-01 22:05:34 -04:00
|
|
|
func (d *downCaser) ReadByte() (c byte, err error) {
|
2011-04-21 14:37:26 -07:00
|
|
|
c, err = d.r.ReadByte()
|
|
|
|
|
if c >= 'A' && c <= 'Z' {
|
|
|
|
|
c += 'a' - 'A'
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-01 22:05:34 -04:00
|
|
|
func (d *downCaser) Read(p []byte) (int, error) {
|
2011-04-21 14:37:26 -07:00
|
|
|
d.t.Fatalf("unexpected Read call on downCaser reader")
|
2011-11-13 22:42:42 -05:00
|
|
|
panic("unreachable")
|
2011-04-21 14:37:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestRawTokenAltEncoding(t *testing.T) {
|
|
|
|
|
sawEncoding := ""
|
2012-01-24 01:10:32 -02:00
|
|
|
d := NewDecoder(strings.NewReader(testInputAltEncoding))
|
|
|
|
|
d.CharsetReader = func(charset string, input io.Reader) (io.Reader, error) {
|
2011-04-21 14:37:26 -07:00
|
|
|
sawEncoding = charset
|
|
|
|
|
if charset != "x-testing-uppercase" {
|
|
|
|
|
t.Fatalf("unexpected charset %q", charset)
|
|
|
|
|
}
|
|
|
|
|
return &downCaser{t, input.(io.ByteReader)}, nil
|
|
|
|
|
}
|
2012-01-24 01:10:32 -02:00
|
|
|
testRawToken(t, d, rawTokensAltEncoding)
|
2011-04-21 14:37:26 -07:00
|
|
|
}
|
2009-10-05 15:00:50 -07:00
|
|
|
|
2011-04-21 14:37:26 -07:00
|
|
|
func TestRawTokenAltEncodingNoConverter(t *testing.T) {
|
2012-01-24 01:10:32 -02:00
|
|
|
d := NewDecoder(strings.NewReader(testInputAltEncoding))
|
|
|
|
|
token, err := d.RawToken()
|
2011-04-21 14:37:26 -07:00
|
|
|
if token == nil {
|
|
|
|
|
t.Fatalf("expected a token on first RawToken call")
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
2012-01-24 01:10:32 -02:00
|
|
|
token, err = d.RawToken()
|
2011-04-21 14:37:26 -07:00
|
|
|
if token != nil {
|
|
|
|
|
t.Errorf("expected a nil token; got %#v", token)
|
|
|
|
|
}
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatalf("expected an error on second RawToken call")
|
|
|
|
|
}
|
|
|
|
|
const encoding = "x-testing-uppercase"
|
2011-11-01 22:05:34 -04:00
|
|
|
if !strings.Contains(err.Error(), encoding) {
|
2011-04-21 14:37:26 -07:00
|
|
|
t.Errorf("expected error to contain %q; got error: %v",
|
|
|
|
|
encoding, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-24 01:10:32 -02:00
|
|
|
func testRawToken(t *testing.T, d *Decoder, rawTokens []Token) {
|
2009-10-05 15:00:50 -07:00
|
|
|
for i, want := range rawTokens {
|
2012-01-24 01:10:32 -02:00
|
|
|
have, err := d.RawToken()
|
2009-10-05 15:00:50 -07:00
|
|
|
if err != nil {
|
2009-11-09 12:07:39 -08:00
|
|
|
t.Fatalf("token %d: unexpected error: %s", i, err)
|
2009-10-05 15:00:50 -07:00
|
|
|
}
|
|
|
|
|
if !reflect.DeepEqual(have, want) {
|
2012-05-17 00:04:00 -03:00
|
|
|
var shave, swant string
|
|
|
|
|
if _, ok := have.(CharData); ok {
|
|
|
|
|
shave = fmt.Sprintf("CharData(%q)", have)
|
|
|
|
|
} else {
|
|
|
|
|
shave = fmt.Sprintf("%#v", have)
|
|
|
|
|
}
|
|
|
|
|
if _, ok := want.(CharData); ok {
|
|
|
|
|
swant = fmt.Sprintf("CharData(%q)", want)
|
|
|
|
|
} else {
|
|
|
|
|
swant = fmt.Sprintf("%#v", want)
|
|
|
|
|
}
|
|
|
|
|
t.Errorf("token %d = %s, want %s", i, shave, swant)
|
2011-02-28 14:09:04 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ensure that directives (specifically !DOCTYPE) include the complete
|
|
|
|
|
// text of any nested directives, noting that < and > do not change
|
|
|
|
|
// nesting depth if they are in single or double quotes.
|
|
|
|
|
|
|
|
|
|
var nestedDirectivesInput = `
|
|
|
|
|
<!DOCTYPE [<!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]>
|
|
|
|
|
<!DOCTYPE [<!ENTITY xlt ">">]>
|
|
|
|
|
<!DOCTYPE [<!ENTITY xlt "<">]>
|
|
|
|
|
<!DOCTYPE [<!ENTITY xlt '>'>]>
|
|
|
|
|
<!DOCTYPE [<!ENTITY xlt '<'>]>
|
|
|
|
|
<!DOCTYPE [<!ENTITY xlt '">'>]>
|
|
|
|
|
<!DOCTYPE [<!ENTITY xlt "'<">]>
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
var nestedDirectivesTokens = []Token{
|
2011-11-22 12:30:02 -05:00
|
|
|
CharData("\n"),
|
|
|
|
|
Directive(`DOCTYPE [<!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]`),
|
|
|
|
|
CharData("\n"),
|
|
|
|
|
Directive(`DOCTYPE [<!ENTITY xlt ">">]`),
|
|
|
|
|
CharData("\n"),
|
|
|
|
|
Directive(`DOCTYPE [<!ENTITY xlt "<">]`),
|
|
|
|
|
CharData("\n"),
|
|
|
|
|
Directive(`DOCTYPE [<!ENTITY xlt '>'>]`),
|
|
|
|
|
CharData("\n"),
|
|
|
|
|
Directive(`DOCTYPE [<!ENTITY xlt '<'>]`),
|
|
|
|
|
CharData("\n"),
|
|
|
|
|
Directive(`DOCTYPE [<!ENTITY xlt '">'>]`),
|
|
|
|
|
CharData("\n"),
|
|
|
|
|
Directive(`DOCTYPE [<!ENTITY xlt "'<">]`),
|
|
|
|
|
CharData("\n"),
|
2011-02-28 14:09:04 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestNestedDirectives(t *testing.T) {
|
2012-01-24 01:10:32 -02:00
|
|
|
d := NewDecoder(strings.NewReader(nestedDirectivesInput))
|
2011-02-28 14:09:04 -05:00
|
|
|
|
|
|
|
|
for i, want := range nestedDirectivesTokens {
|
2012-01-24 01:10:32 -02:00
|
|
|
have, err := d.Token()
|
2011-02-28 14:09:04 -05:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("token %d: unexpected error: %s", i, err)
|
|
|
|
|
}
|
|
|
|
|
if !reflect.DeepEqual(have, want) {
|
2009-11-09 12:07:39 -08:00
|
|
|
t.Errorf("token %d = %#v want %#v", i, have, want)
|
2009-10-05 15:00:50 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestToken(t *testing.T) {
|
2012-01-24 01:10:32 -02:00
|
|
|
d := NewDecoder(strings.NewReader(testInput))
|
2012-10-21 20:33:24 -04:00
|
|
|
d.Entity = testEntity
|
2009-10-05 15:00:50 -07:00
|
|
|
|
|
|
|
|
for i, want := range cookedTokens {
|
2012-01-24 01:10:32 -02:00
|
|
|
have, err := d.Token()
|
2009-10-05 15:00:50 -07:00
|
|
|
if err != nil {
|
2009-11-09 12:07:39 -08:00
|
|
|
t.Fatalf("token %d: unexpected error: %s", i, err)
|
2009-10-05 15:00:50 -07:00
|
|
|
}
|
|
|
|
|
if !reflect.DeepEqual(have, want) {
|
2009-11-09 12:07:39 -08:00
|
|
|
t.Errorf("token %d = %#v want %#v", i, have, want)
|
2009-10-05 15:00:50 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-12-14 19:28:36 -08:00
|
|
|
|
|
|
|
|
func TestSyntax(t *testing.T) {
|
|
|
|
|
for i := range xmlInput {
|
2012-01-24 01:10:32 -02:00
|
|
|
d := NewDecoder(strings.NewReader(xmlInput[i]))
|
2011-11-01 22:05:34 -04:00
|
|
|
var err error
|
2012-01-24 01:10:32 -02:00
|
|
|
for _, err = d.Token(); err == nil; _, err = d.Token() {
|
2009-12-14 19:28:36 -08:00
|
|
|
}
|
2010-03-23 13:20:16 -07:00
|
|
|
if _, ok := err.(*SyntaxError); !ok {
|
2009-12-14 19:28:36 -08:00
|
|
|
t.Fatalf(`xmlInput "%s": expected SyntaxError not received`, xmlInput[i])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-01-27 21:13:22 -08:00
|
|
|
|
2010-02-02 11:53:10 +11:00
|
|
|
type allScalars struct {
|
2011-04-28 15:43:42 -04:00
|
|
|
True1 bool
|
|
|
|
|
True2 bool
|
|
|
|
|
False1 bool
|
|
|
|
|
False2 bool
|
|
|
|
|
Int int
|
|
|
|
|
Int8 int8
|
|
|
|
|
Int16 int16
|
|
|
|
|
Int32 int32
|
|
|
|
|
Int64 int64
|
|
|
|
|
Uint int
|
|
|
|
|
Uint8 uint8
|
|
|
|
|
Uint16 uint16
|
|
|
|
|
Uint32 uint32
|
|
|
|
|
Uint64 uint64
|
|
|
|
|
Uintptr uintptr
|
|
|
|
|
Float32 float32
|
|
|
|
|
Float64 float64
|
|
|
|
|
String string
|
|
|
|
|
PtrString *string
|
2010-02-02 11:53:10 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var all = allScalars{
|
2011-04-28 15:43:42 -04:00
|
|
|
True1: true,
|
|
|
|
|
True2: true,
|
|
|
|
|
False1: false,
|
|
|
|
|
False2: false,
|
|
|
|
|
Int: 1,
|
|
|
|
|
Int8: -2,
|
|
|
|
|
Int16: 3,
|
|
|
|
|
Int32: -4,
|
|
|
|
|
Int64: 5,
|
|
|
|
|
Uint: 6,
|
|
|
|
|
Uint8: 7,
|
|
|
|
|
Uint16: 8,
|
|
|
|
|
Uint32: 9,
|
|
|
|
|
Uint64: 10,
|
|
|
|
|
Uintptr: 11,
|
|
|
|
|
Float32: 13.0,
|
|
|
|
|
Float64: 14.0,
|
|
|
|
|
String: "15",
|
|
|
|
|
PtrString: &sixteen,
|
2010-02-02 11:53:10 +11:00
|
|
|
}
|
|
|
|
|
|
2011-04-28 15:43:42 -04:00
|
|
|
var sixteen = "16"
|
|
|
|
|
|
2010-02-02 11:53:10 +11:00
|
|
|
const testScalarsInput = `<allscalars>
|
xml: major Go 1 fixup
This CL improves the xml package in the following ways:
- makes its interface match established conventions
- brings Marshal and Unmarshal closer together
- fixes a large number of bugs and adds tests
- improves speed significantly
- organizes and simplifies the code
Fixes #2426.
Fixes #2406.
Fixes #1989.
What follows is a detailed list of those changes.
- All matching is case sensitive without special processing
to the field name or xml tag in an attempt to match them.
Customize the field tag as desired to match the correct XML
elements.
- Flags are ",flag" rather than "flag". The names "attr",
"chardata", etc, may be used to name actual XML elements.
- Overriding of attribute names is possible with "name,attr".
- Attribute fields are marshalled properly if they have
non-string types. Previously they were unmarshalled, but were
ignored at marshalling time.
- Comment fields tagged with ",comment" are marshalled properly,
rather than being marshalled as normal fields.
- The handling of the Any field has been replaced by the ",any"
flag to avoid unexpected results when using the field name for
other purposes, and has also been fixed to interact properly
with name paths. Previously the feature would not function
if any field in the type had a name path in its tag.
- Embedded struct support fixed and cleaned so it works when
marshalling and also when using field paths deeper than one level.
- Conflict reporting on field names have been expanded to cover
all fields. Previously it'd catch only conflicts of paths
deeper than one level. Also interacts correctly with embedded
structs now.
- A trailing '>' is disallowed in xml tags. It used to be
supported for removing the ambiguity between "attr" and "attr>",
but the marshalling support for that was broken, and it's now
unnecessary. Use "name" instead of "name>".
- Fixed docs to point out that a XMLName doesn't have to be
an xml.Name (e.g. a struct{} is a good fit too). The code was
already working like that.
- Fixed asymmetry in the precedence of XML element names between
marshalling and unmarshalling. Marshal would consider the XMLName
of the field type before the field tag, while unmarshalling would
do the opposite. Now both respect the tag of the XMLName field
first, and a nice error message is provided in case an attempt
is made to name a field with its tag in a way that would
conflict with the underlying type's XMLName field.
- Do not marshal broken "<???>" tags when in doubt. Use the type
name, and error out if that's not possible.
- Do not break down unmarshalling if there's an interface{} field
in a struct.
- Significant speed boost due to caching of type metadata and
overall allocation clean ups. The following timings reflect
processing of the the atom test data:
Old:
BenchmarkMarshal 50000 48798 ns/op
BenchmarkUnmarshal 5000 357174 ns/op
New:
BenchmarkMarshal 100000 19799 ns/op
BenchmarkUnmarshal 10000 128525 ns/op
R=cw, gustavo, kevlar, adg, rogpeppe, fullung, christoph, rsc
CC=golang-dev
https://golang.org/cl/5503078
2012-01-13 11:05:19 +01:00
|
|
|
<True1>true</True1>
|
|
|
|
|
<True2>1</True2>
|
|
|
|
|
<False1>false</False1>
|
|
|
|
|
<False2>0</False2>
|
|
|
|
|
<Int>1</Int>
|
|
|
|
|
<Int8>-2</Int8>
|
|
|
|
|
<Int16>3</Int16>
|
|
|
|
|
<Int32>-4</Int32>
|
|
|
|
|
<Int64>5</Int64>
|
|
|
|
|
<Uint>6</Uint>
|
|
|
|
|
<Uint8>7</Uint8>
|
|
|
|
|
<Uint16>8</Uint16>
|
|
|
|
|
<Uint32>9</Uint32>
|
|
|
|
|
<Uint64>10</Uint64>
|
|
|
|
|
<Uintptr>11</Uintptr>
|
|
|
|
|
<Float>12.0</Float>
|
|
|
|
|
<Float32>13.0</Float32>
|
|
|
|
|
<Float64>14.0</Float64>
|
|
|
|
|
<String>15</String>
|
|
|
|
|
<PtrString>16</PtrString>
|
2010-02-02 11:53:10 +11:00
|
|
|
</allscalars>`
|
|
|
|
|
|
|
|
|
|
func TestAllScalars(t *testing.T) {
|
|
|
|
|
var a allScalars
|
2012-01-24 01:10:32 -02:00
|
|
|
err := Unmarshal([]byte(testScalarsInput), &a)
|
2010-02-02 11:53:10 +11:00
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if !reflect.DeepEqual(a, all) {
|
2011-04-28 15:43:42 -04:00
|
|
|
t.Errorf("have %+v want %+v", a, all)
|
2010-02-02 11:53:10 +11:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-27 21:13:22 -08:00
|
|
|
type item struct {
|
|
|
|
|
Field_a string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestIssue569(t *testing.T) {
|
xml: major Go 1 fixup
This CL improves the xml package in the following ways:
- makes its interface match established conventions
- brings Marshal and Unmarshal closer together
- fixes a large number of bugs and adds tests
- improves speed significantly
- organizes and simplifies the code
Fixes #2426.
Fixes #2406.
Fixes #1989.
What follows is a detailed list of those changes.
- All matching is case sensitive without special processing
to the field name or xml tag in an attempt to match them.
Customize the field tag as desired to match the correct XML
elements.
- Flags are ",flag" rather than "flag". The names "attr",
"chardata", etc, may be used to name actual XML elements.
- Overriding of attribute names is possible with "name,attr".
- Attribute fields are marshalled properly if they have
non-string types. Previously they were unmarshalled, but were
ignored at marshalling time.
- Comment fields tagged with ",comment" are marshalled properly,
rather than being marshalled as normal fields.
- The handling of the Any field has been replaced by the ",any"
flag to avoid unexpected results when using the field name for
other purposes, and has also been fixed to interact properly
with name paths. Previously the feature would not function
if any field in the type had a name path in its tag.
- Embedded struct support fixed and cleaned so it works when
marshalling and also when using field paths deeper than one level.
- Conflict reporting on field names have been expanded to cover
all fields. Previously it'd catch only conflicts of paths
deeper than one level. Also interacts correctly with embedded
structs now.
- A trailing '>' is disallowed in xml tags. It used to be
supported for removing the ambiguity between "attr" and "attr>",
but the marshalling support for that was broken, and it's now
unnecessary. Use "name" instead of "name>".
- Fixed docs to point out that a XMLName doesn't have to be
an xml.Name (e.g. a struct{} is a good fit too). The code was
already working like that.
- Fixed asymmetry in the precedence of XML element names between
marshalling and unmarshalling. Marshal would consider the XMLName
of the field type before the field tag, while unmarshalling would
do the opposite. Now both respect the tag of the XMLName field
first, and a nice error message is provided in case an attempt
is made to name a field with its tag in a way that would
conflict with the underlying type's XMLName field.
- Do not marshal broken "<???>" tags when in doubt. Use the type
name, and error out if that's not possible.
- Do not break down unmarshalling if there's an interface{} field
in a struct.
- Significant speed boost due to caching of type metadata and
overall allocation clean ups. The following timings reflect
processing of the the atom test data:
Old:
BenchmarkMarshal 50000 48798 ns/op
BenchmarkUnmarshal 5000 357174 ns/op
New:
BenchmarkMarshal 100000 19799 ns/op
BenchmarkUnmarshal 10000 128525 ns/op
R=cw, gustavo, kevlar, adg, rogpeppe, fullung, christoph, rsc
CC=golang-dev
https://golang.org/cl/5503078
2012-01-13 11:05:19 +01:00
|
|
|
data := `<item><Field_a>abcd</Field_a></item>`
|
2010-01-27 21:13:22 -08:00
|
|
|
var i item
|
2012-01-24 01:10:32 -02:00
|
|
|
err := Unmarshal([]byte(data), &i)
|
2010-01-27 21:13:22 -08:00
|
|
|
|
|
|
|
|
if err != nil || i.Field_a != "abcd" {
|
2010-12-07 16:42:54 -05:00
|
|
|
t.Fatal("Expecting abcd")
|
2010-01-27 21:13:22 -08:00
|
|
|
}
|
|
|
|
|
}
|
2010-02-18 23:32:55 -08:00
|
|
|
|
|
|
|
|
func TestUnquotedAttrs(t *testing.T) {
|
|
|
|
|
data := "<tag attr=azAZ09:-_\t>"
|
2012-01-24 01:10:32 -02:00
|
|
|
d := NewDecoder(strings.NewReader(data))
|
|
|
|
|
d.Strict = false
|
|
|
|
|
token, err := d.Token()
|
2010-03-23 13:20:16 -07:00
|
|
|
if _, ok := err.(*SyntaxError); ok {
|
2010-02-18 23:32:55 -08:00
|
|
|
t.Errorf("Unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if token.(StartElement).Name.Local != "tag" {
|
|
|
|
|
t.Errorf("Unexpected tag name: %v", token.(StartElement).Name.Local)
|
|
|
|
|
}
|
|
|
|
|
attr := token.(StartElement).Attr[0]
|
|
|
|
|
if attr.Value != "azAZ09:-_" {
|
|
|
|
|
t.Errorf("Unexpected attribute value: %v", attr.Value)
|
|
|
|
|
}
|
|
|
|
|
if attr.Name.Local != "attr" {
|
|
|
|
|
t.Errorf("Unexpected attribute name: %v", attr.Name.Local)
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-03-23 09:43:20 -07:00
|
|
|
|
2011-06-16 12:56:49 -04:00
|
|
|
func TestValuelessAttrs(t *testing.T) {
|
|
|
|
|
tests := [][3]string{
|
|
|
|
|
{"<p nowrap>", "p", "nowrap"},
|
|
|
|
|
{"<p nowrap >", "p", "nowrap"},
|
|
|
|
|
{"<input checked/>", "input", "checked"},
|
|
|
|
|
{"<input checked />", "input", "checked"},
|
|
|
|
|
}
|
|
|
|
|
for _, test := range tests {
|
2012-01-24 01:10:32 -02:00
|
|
|
d := NewDecoder(strings.NewReader(test[0]))
|
|
|
|
|
d.Strict = false
|
|
|
|
|
token, err := d.Token()
|
2011-06-16 12:56:49 -04:00
|
|
|
if _, ok := err.(*SyntaxError); ok {
|
|
|
|
|
t.Errorf("Unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if token.(StartElement).Name.Local != test[1] {
|
|
|
|
|
t.Errorf("Unexpected tag name: %v", token.(StartElement).Name.Local)
|
|
|
|
|
}
|
|
|
|
|
attr := token.(StartElement).Attr[0]
|
|
|
|
|
if attr.Value != test[2] {
|
|
|
|
|
t.Errorf("Unexpected attribute value: %v", attr.Value)
|
|
|
|
|
}
|
|
|
|
|
if attr.Name.Local != test[2] {
|
|
|
|
|
t.Errorf("Unexpected attribute name: %v", attr.Name.Local)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-23 09:43:20 -07:00
|
|
|
func TestCopyTokenCharData(t *testing.T) {
|
|
|
|
|
data := []byte("same data")
|
|
|
|
|
var tok1 Token = CharData(data)
|
|
|
|
|
tok2 := CopyToken(tok1)
|
|
|
|
|
if !reflect.DeepEqual(tok1, tok2) {
|
|
|
|
|
t.Error("CopyToken(CharData) != CharData")
|
|
|
|
|
}
|
|
|
|
|
data[1] = 'o'
|
|
|
|
|
if reflect.DeepEqual(tok1, tok2) {
|
|
|
|
|
t.Error("CopyToken(CharData) uses same buffer.")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestCopyTokenStartElement(t *testing.T) {
|
2010-10-22 10:06:33 -07:00
|
|
|
elt := StartElement{Name{"", "hello"}, []Attr{{Name{"", "lang"}, "en"}}}
|
2010-03-23 09:43:20 -07:00
|
|
|
var tok1 Token = elt
|
|
|
|
|
tok2 := CopyToken(tok1)
|
2011-11-22 12:31:33 -05:00
|
|
|
if tok1.(StartElement).Attr[0].Value != "en" {
|
|
|
|
|
t.Error("CopyToken overwrote Attr[0]")
|
|
|
|
|
}
|
2010-03-23 09:43:20 -07:00
|
|
|
if !reflect.DeepEqual(tok1, tok2) {
|
|
|
|
|
t.Error("CopyToken(StartElement) != StartElement")
|
|
|
|
|
}
|
2011-11-22 12:31:33 -05:00
|
|
|
tok1.(StartElement).Attr[0] = Attr{Name{"", "lang"}, "de"}
|
2010-03-23 09:43:20 -07:00
|
|
|
if reflect.DeepEqual(tok1, tok2) {
|
|
|
|
|
t.Error("CopyToken(CharData) uses same buffer.")
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-03-23 13:20:16 -07:00
|
|
|
|
|
|
|
|
func TestSyntaxErrorLineNum(t *testing.T) {
|
|
|
|
|
testInput := "<P>Foo<P>\n\n<P>Bar</>\n"
|
2012-01-24 01:10:32 -02:00
|
|
|
d := NewDecoder(strings.NewReader(testInput))
|
2011-11-01 22:05:34 -04:00
|
|
|
var err error
|
2012-01-24 01:10:32 -02:00
|
|
|
for _, err = d.Token(); err == nil; _, err = d.Token() {
|
2010-03-23 13:20:16 -07:00
|
|
|
}
|
|
|
|
|
synerr, ok := err.(*SyntaxError)
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Error("Expected SyntaxError.")
|
|
|
|
|
}
|
|
|
|
|
if synerr.Line != 3 {
|
|
|
|
|
t.Error("SyntaxError didn't have correct line number.")
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-04-28 19:29:20 -07:00
|
|
|
|
|
|
|
|
func TestTrailingRawToken(t *testing.T) {
|
|
|
|
|
input := `<FOO></FOO> `
|
2012-01-24 01:10:32 -02:00
|
|
|
d := NewDecoder(strings.NewReader(input))
|
2011-11-01 22:05:34 -04:00
|
|
|
var err error
|
2012-01-24 01:10:32 -02:00
|
|
|
for _, err = d.RawToken(); err == nil; _, err = d.RawToken() {
|
2010-04-28 19:29:20 -07:00
|
|
|
}
|
2011-11-01 22:05:34 -04:00
|
|
|
if err != io.EOF {
|
2012-01-24 01:10:32 -02:00
|
|
|
t.Fatalf("d.RawToken() = _, %v, want _, io.EOF", err)
|
2010-04-28 19:29:20 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestTrailingToken(t *testing.T) {
|
|
|
|
|
input := `<FOO></FOO> `
|
2012-01-24 01:10:32 -02:00
|
|
|
d := NewDecoder(strings.NewReader(input))
|
2011-11-01 22:05:34 -04:00
|
|
|
var err error
|
2012-01-24 01:10:32 -02:00
|
|
|
for _, err = d.Token(); err == nil; _, err = d.Token() {
|
2010-04-28 19:29:20 -07:00
|
|
|
}
|
2011-11-01 22:05:34 -04:00
|
|
|
if err != io.EOF {
|
2012-01-24 01:10:32 -02:00
|
|
|
t.Fatalf("d.Token() = _, %v, want _, io.EOF", err)
|
2010-04-28 19:29:20 -07:00
|
|
|
}
|
|
|
|
|
}
|
2010-09-24 12:23:01 -04:00
|
|
|
|
|
|
|
|
func TestEntityInsideCDATA(t *testing.T) {
|
|
|
|
|
input := `<test><![CDATA[ &val=foo ]]></test>`
|
2012-01-24 01:10:32 -02:00
|
|
|
d := NewDecoder(strings.NewReader(input))
|
2011-11-01 22:05:34 -04:00
|
|
|
var err error
|
2012-01-24 01:10:32 -02:00
|
|
|
for _, err = d.Token(); err == nil; _, err = d.Token() {
|
2010-09-24 12:23:01 -04:00
|
|
|
}
|
2011-11-01 22:05:34 -04:00
|
|
|
if err != io.EOF {
|
2012-01-24 01:10:32 -02:00
|
|
|
t.Fatalf("d.Token() = _, %v, want _, io.EOF", err)
|
2010-09-24 12:23:01 -04:00
|
|
|
}
|
|
|
|
|
}
|
2010-12-09 14:51:01 -05:00
|
|
|
|
|
|
|
|
// The last three tests (respectively one for characters in attribute
|
|
|
|
|
// names and two for character entities) pass not because of code
|
|
|
|
|
// changed for issue 1259, but instead pass with the given messages
|
2012-01-24 01:10:32 -02:00
|
|
|
// from other parts of xml.Decoder. I provide these to note the
|
2010-12-09 14:51:01 -05:00
|
|
|
// current behavior of situations where one might think that character
|
|
|
|
|
// range checking would detect the error, but it does not in fact.
|
|
|
|
|
|
|
|
|
|
var characterTests = []struct {
|
|
|
|
|
in string
|
|
|
|
|
err string
|
|
|
|
|
}{
|
|
|
|
|
{"\x12<doc/>", "illegal character code U+0012"},
|
|
|
|
|
{"<?xml version=\"1.0\"?>\x0b<doc/>", "illegal character code U+000B"},
|
|
|
|
|
{"\xef\xbf\xbe<doc/>", "illegal character code U+FFFE"},
|
|
|
|
|
{"<?xml version=\"1.0\"?><doc>\r\n<hiya/>\x07<toots/></doc>", "illegal character code U+0007"},
|
|
|
|
|
{"<?xml version=\"1.0\"?><doc \x12='value'>what's up</doc>", "expected attribute name in element"},
|
2012-05-17 00:04:00 -03:00
|
|
|
{"<doc>&\x01;</doc>", "invalid character entity & (no semicolon)"},
|
|
|
|
|
{"<doc>&\xef\xbf\xbe;</doc>", "invalid character entity & (no semicolon)"},
|
2010-12-09 14:51:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestDisallowedCharacters(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
for i, tt := range characterTests {
|
2012-01-24 01:10:32 -02:00
|
|
|
d := NewDecoder(strings.NewReader(tt.in))
|
2011-11-01 22:05:34 -04:00
|
|
|
var err error
|
2010-12-09 14:51:01 -05:00
|
|
|
|
|
|
|
|
for err == nil {
|
2012-01-24 01:10:32 -02:00
|
|
|
_, err = d.Token()
|
2010-12-09 14:51:01 -05:00
|
|
|
}
|
|
|
|
|
synerr, ok := err.(*SyntaxError)
|
|
|
|
|
if !ok {
|
2012-01-24 01:10:32 -02:00
|
|
|
t.Fatalf("input %d d.Token() = _, %v, want _, *SyntaxError", i, err)
|
2010-12-09 14:51:01 -05:00
|
|
|
}
|
|
|
|
|
if synerr.Msg != tt.err {
|
|
|
|
|
t.Fatalf("input %d synerr.Msg wrong: want '%s', got '%s'", i, tt.err, synerr.Msg)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-04-21 14:37:26 -07:00
|
|
|
|
|
|
|
|
type procInstEncodingTest struct {
|
|
|
|
|
expect, got string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var procInstTests = []struct {
|
|
|
|
|
input, expect string
|
|
|
|
|
}{
|
|
|
|
|
{`version="1.0" encoding="utf-8"`, "utf-8"},
|
|
|
|
|
{`version="1.0" encoding='utf-8'`, "utf-8"},
|
|
|
|
|
{`version="1.0" encoding='utf-8' `, "utf-8"},
|
|
|
|
|
{`version="1.0" encoding=utf-8`, ""},
|
|
|
|
|
{`encoding="FOO" `, "FOO"},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestProcInstEncoding(t *testing.T) {
|
|
|
|
|
for _, test := range procInstTests {
|
|
|
|
|
got := procInstEncoding(test.input)
|
|
|
|
|
if got != test.expect {
|
|
|
|
|
t.Errorf("procInstEncoding(%q) = %q; want %q", test.input, got, test.expect)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-08-31 18:09:31 -04:00
|
|
|
|
|
|
|
|
// Ensure that directives with comments include the complete
|
|
|
|
|
// text of any nested directives.
|
|
|
|
|
|
|
|
|
|
var directivesWithCommentsInput = `
|
|
|
|
|
<!DOCTYPE [<!-- a comment --><!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]>
|
|
|
|
|
<!DOCTYPE [<!ENTITY go "Golang"><!-- a comment-->]>
|
|
|
|
|
<!DOCTYPE <!-> <!> <!----> <!-->--> <!--->--> [<!ENTITY go "Golang"><!-- a comment-->]>
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
var directivesWithCommentsTokens = []Token{
|
|
|
|
|
CharData("\n"),
|
|
|
|
|
Directive(`DOCTYPE [<!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]`),
|
|
|
|
|
CharData("\n"),
|
|
|
|
|
Directive(`DOCTYPE [<!ENTITY go "Golang">]`),
|
|
|
|
|
CharData("\n"),
|
|
|
|
|
Directive(`DOCTYPE <!-> <!> [<!ENTITY go "Golang">]`),
|
|
|
|
|
CharData("\n"),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestDirectivesWithComments(t *testing.T) {
|
|
|
|
|
d := NewDecoder(strings.NewReader(directivesWithCommentsInput))
|
|
|
|
|
|
|
|
|
|
for i, want := range directivesWithCommentsTokens {
|
|
|
|
|
have, err := d.Token()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("token %d: unexpected error: %s", i, err)
|
|
|
|
|
}
|
|
|
|
|
if !reflect.DeepEqual(have, want) {
|
|
|
|
|
t.Errorf("token %d = %#v want %#v", i, have, want)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-02-20 14:41:23 -08:00
|
|
|
|
|
|
|
|
// Writer whose Write method always returns an error.
|
|
|
|
|
type errWriter struct{}
|
|
|
|
|
|
|
|
|
|
func (errWriter) Write(p []byte) (n int, err error) { return 0, fmt.Errorf("unwritable") }
|
|
|
|
|
|
|
|
|
|
func TestEscapeTextIOErrors(t *testing.T) {
|
|
|
|
|
expectErr := "unwritable"
|
|
|
|
|
err := EscapeText(errWriter{}, []byte{'A'})
|
|
|
|
|
|
|
|
|
|
if err == nil || err.Error() != expectErr {
|
|
|
|
|
t.Errorf("EscapeTest = [error] %v, want %v", err, expectErr)
|
|
|
|
|
}
|
|
|
|
|
}
|