// 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 ( "bytes" "fmt" "io" "reflect" "strings" "testing" "unicode/utf8" ) type toks struct { earlyEOF bool t []Token } func (t *toks) Token() (Token, error) { if len(t.t) == 0 { return nil, io.EOF } var tok Token tok, t.t = t.t[0], t.t[1:] if t.earlyEOF && len(t.t) == 0 { return tok, io.EOF } return tok, nil } func TestDecodeEOF(t *testing.T) { start := StartElement{Name: Name{Local: "test"}} tests := []struct { name string tokens []Token ok bool }{ { name: "OK", tokens: []Token{ start, start.End(), }, ok: true, }, { name: "Malformed", tokens: []Token{ start, StartElement{Name: Name{Local: "bad"}}, start.End(), }, ok: false, }, } for _, tc := range tests { for _, eof := range []bool{true, false} { name := fmt.Sprintf("%s/earlyEOF=%v", tc.name, eof) t.Run(name, func(t *testing.T) { d := NewTokenDecoder(&toks{ earlyEOF: eof, t: tc.tokens, }) err := d.Decode(&struct { XMLName Name `xml:"test"` }{}) if tc.ok && err != nil { t.Fatalf("d.Decode: expected nil error, got %v", err) } if _, ok := err.(*SyntaxError); !tc.ok && !ok { t.Errorf("d.Decode: expected syntax error, got %v", err) } }) } } } type toksNil struct { returnEOF bool t []Token } func (t *toksNil) Token() (Token, error) { if len(t.t) == 0 { if !t.returnEOF { // Return nil, nil before returning an EOF. It's legal, but // discouraged. t.returnEOF = true return nil, nil } return nil, io.EOF } var tok Token tok, t.t = t.t[0], t.t[1:] return tok, nil } func TestDecodeNilToken(t *testing.T) { for _, strict := range []bool{true, false} { name := fmt.Sprintf("Strict=%v", strict) t.Run(name, func(t *testing.T) { start := StartElement{Name: Name{Local: "test"}} bad := StartElement{Name: Name{Local: "bad"}} d := NewTokenDecoder(&toksNil{ // Malformed t: []Token{start, bad, start.End()}, }) d.Strict = strict err := d.Decode(&struct { XMLName Name `xml:"test"` }{}) if _, ok := err.(*SyntaxError); !ok { t.Errorf("d.Decode: expected syntax error, got %v", err) } }) } } const testInput = `
", "p", "nowrap"}, {"
", "p", "nowrap"}, {"", "input", "checked"}, {"", "input", "checked"}, } for _, test := range tests { d := NewDecoder(strings.NewReader(test[0])) d.Strict = false token, err := d.Token() 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) } } } 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) { elt := StartElement{Name{"", "hello"}, []Attr{{Name{"", "lang"}, "en"}}} var tok1 Token = elt tok2 := CopyToken(tok1) if tok1.(StartElement).Attr[0].Value != "en" { t.Error("CopyToken overwrote Attr[0]") } if !reflect.DeepEqual(tok1, tok2) { t.Error("CopyToken(StartElement) != StartElement") } tok1.(StartElement).Attr[0] = Attr{Name{"", "lang"}, "de"} if reflect.DeepEqual(tok1, tok2) { t.Error("CopyToken(CharData) uses same buffer.") } } func TestSyntaxErrorLineNum(t *testing.T) { testInput := "
Foo
\n\n
Bar>\n"
d := NewDecoder(strings.NewReader(testInput))
var err error
for _, err = d.Token(); err == nil; _, err = d.Token() {
}
synerr, ok := err.(*SyntaxError)
if !ok {
t.Error("Expected SyntaxError.")
}
if synerr.Line != 3 {
t.Error("SyntaxError didn't have correct line number.")
}
}
func TestTrailingRawToken(t *testing.T) {
input := `
`))
d2 := NewTokenDecoder(d)
if d != d2 {
t.Error("NewTokenDecoder did not detect underlying Decoder")
}
}
func TestWrapDecoder(t *testing.T) {
d := NewDecoder(strings.NewReader(`[Re-enter Clown with a letter, and FABIAN]
`))
m := tokenMap(func(t Token) Token {
switch tok := t.(type) {
case StartElement:
if tok.Name.Local == "quote" {
tok.Name.Local = "blocking"
return tok
}
case EndElement:
if tok.Name.Local == "quote" {
tok.Name.Local = "blocking"
return tok
}
}
return t
})
d = NewTokenDecoder(m(d))
o := struct {
XMLName Name `xml:"blocking"`
Chardata string `xml:",chardata"`
}{}
if err := d.Decode(&o); err != nil {
t.Fatal("Got unexpected error while decoding:", err)
}
if o.Chardata != "[Re-enter Clown with a letter, and FABIAN]" {
t.Fatalf("Got unexpected chardata: `%s`\n", o.Chardata)
}
}
type tokReader struct{}
func (tokReader) Token() (Token, error) {
return StartElement{}, nil
}
type Failure struct{}
func (Failure) UnmarshalXML(*Decoder, StartElement) error {
return nil
}
func TestTokenUnmarshaler(t *testing.T) {
defer func() {
if r := recover(); r != nil {
t.Error("Unexpected panic using custom token unmarshaler")
}
}()
d := NewTokenDecoder(tokReader{})
d.Decode(&Failure{})
}
func testRoundTrip(t *testing.T, input string) {
d := NewDecoder(strings.NewReader(input))
var tokens []Token
var buf bytes.Buffer
e := NewEncoder(&buf)
for {
tok, err := d.Token()
if err == io.EOF {
break
}
if err != nil {
t.Fatalf("invalid input: %v", err)
}
if err := e.EncodeToken(tok); err != nil {
t.Fatalf("failed to re-encode input: %v", err)
}
tokens = append(tokens, CopyToken(tok))
}
if err := e.Flush(); err != nil {
t.Fatal(err)
}
d = NewDecoder(&buf)
for {
tok, err := d.Token()
if err == io.EOF {
break
}
if err != nil {
t.Fatalf("failed to decode output: %v", err)
}
if len(tokens) == 0 {
t.Fatalf("unexpected token: %#v", tok)
}
a, b := tokens[0], tok
if !reflect.DeepEqual(a, b) {
t.Fatalf("token mismatch: %#v vs %#v", a, b)
}
tokens = tokens[1:]
}
if len(tokens) > 0 {
t.Fatalf("lost tokens: %#v", tokens)
}
}
func TestRoundTrip(t *testing.T) {
tests := map[string]string{
"leading colon": `<::Test ::foo="bar"><:::Hello>