Run Format

Source file src/pkg/encoding/xml/xml_test.go

     1	// Copyright 2009 The Go Authors.  All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	
     5	package xml
     6	
     7	import (
     8		"bytes"
     9		"fmt"
    10		"io"
    11		"reflect"
    12		"strings"
    13		"testing"
    14	)
    15	
    16	const testInput = `
    17	<?xml version="1.0" encoding="UTF-8"?>
    18	<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    19	  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    20	<body xmlns:foo="ns1" xmlns="ns2" xmlns:tag="ns3" ` +
    21		"\r\n\t" + `  >
    22	  <hello lang="en">World &lt;&gt;&apos;&quot; &#x767d;&#40300;翔</hello>
    23	  <query>&何; &is-it;</query>
    24	  <goodbye />
    25	  <outer foo:attr="value" xmlns:tag="ns4">
    26	    <inner/>
    27	  </outer>
    28	  <tag:name>
    29	    <![CDATA[Some text here.]]>
    30	  </tag:name>
    31	</body><!-- missing final newline -->`
    32	
    33	var testEntity = map[string]string{"何": "What", "is-it": "is it?"}
    34	
    35	var rawTokens = []Token{
    36		CharData("\n"),
    37		ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)},
    38		CharData("\n"),
    39		Directive(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    40	  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"`),
    41		CharData("\n"),
    42		StartElement{Name{"", "body"}, []Attr{{Name{"xmlns", "foo"}, "ns1"}, {Name{"", "xmlns"}, "ns2"}, {Name{"xmlns", "tag"}, "ns3"}}},
    43		CharData("\n  "),
    44		StartElement{Name{"", "hello"}, []Attr{{Name{"", "lang"}, "en"}}},
    45		CharData("World <>'\" 白鵬翔"),
    46		EndElement{Name{"", "hello"}},
    47		CharData("\n  "),
    48		StartElement{Name{"", "query"}, []Attr{}},
    49		CharData("What is it?"),
    50		EndElement{Name{"", "query"}},
    51		CharData("\n  "),
    52		StartElement{Name{"", "goodbye"}, []Attr{}},
    53		EndElement{Name{"", "goodbye"}},
    54		CharData("\n  "),
    55		StartElement{Name{"", "outer"}, []Attr{{Name{"foo", "attr"}, "value"}, {Name{"xmlns", "tag"}, "ns4"}}},
    56		CharData("\n    "),
    57		StartElement{Name{"", "inner"}, []Attr{}},
    58		EndElement{Name{"", "inner"}},
    59		CharData("\n  "),
    60		EndElement{Name{"", "outer"}},
    61		CharData("\n  "),
    62		StartElement{Name{"tag", "name"}, []Attr{}},
    63		CharData("\n    "),
    64		CharData("Some text here."),
    65		CharData("\n  "),
    66		EndElement{Name{"tag", "name"}},
    67		CharData("\n"),
    68		EndElement{Name{"", "body"}},
    69		Comment(" missing final newline "),
    70	}
    71	
    72	var cookedTokens = []Token{
    73		CharData("\n"),
    74		ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)},
    75		CharData("\n"),
    76		Directive(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    77	  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"`),
    78		CharData("\n"),
    79		StartElement{Name{"ns2", "body"}, []Attr{{Name{"xmlns", "foo"}, "ns1"}, {Name{"", "xmlns"}, "ns2"}, {Name{"xmlns", "tag"}, "ns3"}}},
    80		CharData("\n  "),
    81		StartElement{Name{"ns2", "hello"}, []Attr{{Name{"", "lang"}, "en"}}},
    82		CharData("World <>'\" 白鵬翔"),
    83		EndElement{Name{"ns2", "hello"}},
    84		CharData("\n  "),
    85		StartElement{Name{"ns2", "query"}, []Attr{}},
    86		CharData("What is it?"),
    87		EndElement{Name{"ns2", "query"}},
    88		CharData("\n  "),
    89		StartElement{Name{"ns2", "goodbye"}, []Attr{}},
    90		EndElement{Name{"ns2", "goodbye"}},
    91		CharData("\n  "),
    92		StartElement{Name{"ns2", "outer"}, []Attr{{Name{"ns1", "attr"}, "value"}, {Name{"xmlns", "tag"}, "ns4"}}},
    93		CharData("\n    "),
    94		StartElement{Name{"ns2", "inner"}, []Attr{}},
    95		EndElement{Name{"ns2", "inner"}},
    96		CharData("\n  "),
    97		EndElement{Name{"ns2", "outer"}},
    98		CharData("\n  "),
    99		StartElement{Name{"ns3", "name"}, []Attr{}},
   100		CharData("\n    "),
   101		CharData("Some text here."),
   102		CharData("\n  "),
   103		EndElement{Name{"ns3", "name"}},
   104		CharData("\n"),
   105		EndElement{Name{"ns2", "body"}},
   106		Comment(" missing final newline "),
   107	}
   108	
   109	const testInputAltEncoding = `
   110	<?xml version="1.0" encoding="x-testing-uppercase"?>
   111	<TAG>VALUE</TAG>`
   112	
   113	var rawTokensAltEncoding = []Token{
   114		CharData("\n"),
   115		ProcInst{"xml", []byte(`version="1.0" encoding="x-testing-uppercase"`)},
   116		CharData("\n"),
   117		StartElement{Name{"", "tag"}, []Attr{}},
   118		CharData("value"),
   119		EndElement{Name{"", "tag"}},
   120	}
   121	
   122	var xmlInput = []string{
   123		// unexpected EOF cases
   124		"<",
   125		"<t",
   126		"<t ",
   127		"<t/",
   128		"<!",
   129		"<!-",
   130		"<!--",
   131		"<!--c-",
   132		"<!--c--",
   133		"<!d",
   134		"<t></",
   135		"<t></t",
   136		"<?",
   137		"<?p",
   138		"<t a",
   139		"<t a=",
   140		"<t a='",
   141		"<t a=''",
   142		"<t/><![",
   143		"<t/><![C",
   144		"<t/><![CDATA[d",
   145		"<t/><![CDATA[d]",
   146		"<t/><![CDATA[d]]",
   147	
   148		// other Syntax errors
   149		"<>",
   150		"<t/a",
   151		"<0 />",
   152		"<?0 >",
   153		//	"<!0 >",	// let the Token() caller handle
   154		"</0>",
   155		"<t 0=''>",
   156		"<t a='&'>",
   157		"<t a='<'>",
   158		"<t>&nbspc;</t>",
   159		"<t a>",
   160		"<t a=>",
   161		"<t a=v>",
   162		//	"<![CDATA[d]]>",	// let the Token() caller handle
   163		"<t></e>",
   164		"<t></>",
   165		"<t></t!",
   166		"<t>cdata]]></t>",
   167	}
   168	
   169	func TestRawToken(t *testing.T) {
   170		d := NewDecoder(strings.NewReader(testInput))
   171		d.Entity = testEntity
   172		testRawToken(t, d, rawTokens)
   173	}
   174	
   175	const nonStrictInput = `
   176	<tag>non&entity</tag>
   177	<tag>&unknown;entity</tag>
   178	<tag>&#123</tag>
   179	<tag>&#zzz;</tag>
   180	<tag>&なまえ3;</tag>
   181	<tag>&lt-gt;</tag>
   182	<tag>&;</tag>
   183	<tag>&0a;</tag>
   184	`
   185	
   186	var nonStringEntity = map[string]string{"": "oops!", "0a": "oops!"}
   187	
   188	var nonStrictTokens = []Token{
   189		CharData("\n"),
   190		StartElement{Name{"", "tag"}, []Attr{}},
   191		CharData("non&entity"),
   192		EndElement{Name{"", "tag"}},
   193		CharData("\n"),
   194		StartElement{Name{"", "tag"}, []Attr{}},
   195		CharData("&unknown;entity"),
   196		EndElement{Name{"", "tag"}},
   197		CharData("\n"),
   198		StartElement{Name{"", "tag"}, []Attr{}},
   199		CharData("&#123"),
   200		EndElement{Name{"", "tag"}},
   201		CharData("\n"),
   202		StartElement{Name{"", "tag"}, []Attr{}},
   203		CharData("&#zzz;"),
   204		EndElement{Name{"", "tag"}},
   205		CharData("\n"),
   206		StartElement{Name{"", "tag"}, []Attr{}},
   207		CharData("&なまえ3;"),
   208		EndElement{Name{"", "tag"}},
   209		CharData("\n"),
   210		StartElement{Name{"", "tag"}, []Attr{}},
   211		CharData("&lt-gt;"),
   212		EndElement{Name{"", "tag"}},
   213		CharData("\n"),
   214		StartElement{Name{"", "tag"}, []Attr{}},
   215		CharData("&;"),
   216		EndElement{Name{"", "tag"}},
   217		CharData("\n"),
   218		StartElement{Name{"", "tag"}, []Attr{}},
   219		CharData("&0a;"),
   220		EndElement{Name{"", "tag"}},
   221		CharData("\n"),
   222	}
   223	
   224	func TestNonStrictRawToken(t *testing.T) {
   225		d := NewDecoder(strings.NewReader(nonStrictInput))
   226		d.Strict = false
   227		testRawToken(t, d, nonStrictTokens)
   228	}
   229	
   230	type downCaser struct {
   231		t *testing.T
   232		r io.ByteReader
   233	}
   234	
   235	func (d *downCaser) ReadByte() (c byte, err error) {
   236		c, err = d.r.ReadByte()
   237		if c >= 'A' && c <= 'Z' {
   238			c += 'a' - 'A'
   239		}
   240		return
   241	}
   242	
   243	func (d *downCaser) Read(p []byte) (int, error) {
   244		d.t.Fatalf("unexpected Read call on downCaser reader")
   245		panic("unreachable")
   246	}
   247	
   248	func TestRawTokenAltEncoding(t *testing.T) {
   249		sawEncoding := ""
   250		d := NewDecoder(strings.NewReader(testInputAltEncoding))
   251		d.CharsetReader = func(charset string, input io.Reader) (io.Reader, error) {
   252			sawEncoding = charset
   253			if charset != "x-testing-uppercase" {
   254				t.Fatalf("unexpected charset %q", charset)
   255			}
   256			return &downCaser{t, input.(io.ByteReader)}, nil
   257		}
   258		testRawToken(t, d, rawTokensAltEncoding)
   259	}
   260	
   261	func TestRawTokenAltEncodingNoConverter(t *testing.T) {
   262		d := NewDecoder(strings.NewReader(testInputAltEncoding))
   263		token, err := d.RawToken()
   264		if token == nil {
   265			t.Fatalf("expected a token on first RawToken call")
   266		}
   267		if err != nil {
   268			t.Fatal(err)
   269		}
   270		token, err = d.RawToken()
   271		if token != nil {
   272			t.Errorf("expected a nil token; got %#v", token)
   273		}
   274		if err == nil {
   275			t.Fatalf("expected an error on second RawToken call")
   276		}
   277		const encoding = "x-testing-uppercase"
   278		if !strings.Contains(err.Error(), encoding) {
   279			t.Errorf("expected error to contain %q; got error: %v",
   280				encoding, err)
   281		}
   282	}
   283	
   284	func testRawToken(t *testing.T, d *Decoder, rawTokens []Token) {
   285		for i, want := range rawTokens {
   286			have, err := d.RawToken()
   287			if err != nil {
   288				t.Fatalf("token %d: unexpected error: %s", i, err)
   289			}
   290			if !reflect.DeepEqual(have, want) {
   291				var shave, swant string
   292				if _, ok := have.(CharData); ok {
   293					shave = fmt.Sprintf("CharData(%q)", have)
   294				} else {
   295					shave = fmt.Sprintf("%#v", have)
   296				}
   297				if _, ok := want.(CharData); ok {
   298					swant = fmt.Sprintf("CharData(%q)", want)
   299				} else {
   300					swant = fmt.Sprintf("%#v", want)
   301				}
   302				t.Errorf("token %d = %s, want %s", i, shave, swant)
   303			}
   304		}
   305	}
   306	
   307	// Ensure that directives (specifically !DOCTYPE) include the complete
   308	// text of any nested directives, noting that < and > do not change
   309	// nesting depth if they are in single or double quotes.
   310	
   311	var nestedDirectivesInput = `
   312	<!DOCTYPE [<!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]>
   313	<!DOCTYPE [<!ENTITY xlt ">">]>
   314	<!DOCTYPE [<!ENTITY xlt "<">]>
   315	<!DOCTYPE [<!ENTITY xlt '>'>]>
   316	<!DOCTYPE [<!ENTITY xlt '<'>]>
   317	<!DOCTYPE [<!ENTITY xlt '">'>]>
   318	<!DOCTYPE [<!ENTITY xlt "'<">]>
   319	`
   320	
   321	var nestedDirectivesTokens = []Token{
   322		CharData("\n"),
   323		Directive(`DOCTYPE [<!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]`),
   324		CharData("\n"),
   325		Directive(`DOCTYPE [<!ENTITY xlt ">">]`),
   326		CharData("\n"),
   327		Directive(`DOCTYPE [<!ENTITY xlt "<">]`),
   328		CharData("\n"),
   329		Directive(`DOCTYPE [<!ENTITY xlt '>'>]`),
   330		CharData("\n"),
   331		Directive(`DOCTYPE [<!ENTITY xlt '<'>]`),
   332		CharData("\n"),
   333		Directive(`DOCTYPE [<!ENTITY xlt '">'>]`),
   334		CharData("\n"),
   335		Directive(`DOCTYPE [<!ENTITY xlt "'<">]`),
   336		CharData("\n"),
   337	}
   338	
   339	func TestNestedDirectives(t *testing.T) {
   340		d := NewDecoder(strings.NewReader(nestedDirectivesInput))
   341	
   342		for i, want := range nestedDirectivesTokens {
   343			have, err := d.Token()
   344			if err != nil {
   345				t.Fatalf("token %d: unexpected error: %s", i, err)
   346			}
   347			if !reflect.DeepEqual(have, want) {
   348				t.Errorf("token %d = %#v want %#v", i, have, want)
   349			}
   350		}
   351	}
   352	
   353	func TestToken(t *testing.T) {
   354		d := NewDecoder(strings.NewReader(testInput))
   355		d.Entity = testEntity
   356	
   357		for i, want := range cookedTokens {
   358			have, err := d.Token()
   359			if err != nil {
   360				t.Fatalf("token %d: unexpected error: %s", i, err)
   361			}
   362			if !reflect.DeepEqual(have, want) {
   363				t.Errorf("token %d = %#v want %#v", i, have, want)
   364			}
   365		}
   366	}
   367	
   368	func TestSyntax(t *testing.T) {
   369		for i := range xmlInput {
   370			d := NewDecoder(strings.NewReader(xmlInput[i]))
   371			var err error
   372			for _, err = d.Token(); err == nil; _, err = d.Token() {
   373			}
   374			if _, ok := err.(*SyntaxError); !ok {
   375				t.Fatalf(`xmlInput "%s": expected SyntaxError not received`, xmlInput[i])
   376			}
   377		}
   378	}
   379	
   380	type allScalars struct {
   381		True1     bool
   382		True2     bool
   383		False1    bool
   384		False2    bool
   385		Int       int
   386		Int8      int8
   387		Int16     int16
   388		Int32     int32
   389		Int64     int64
   390		Uint      int
   391		Uint8     uint8
   392		Uint16    uint16
   393		Uint32    uint32
   394		Uint64    uint64
   395		Uintptr   uintptr
   396		Float32   float32
   397		Float64   float64
   398		String    string
   399		PtrString *string
   400	}
   401	
   402	var all = allScalars{
   403		True1:     true,
   404		True2:     true,
   405		False1:    false,
   406		False2:    false,
   407		Int:       1,
   408		Int8:      -2,
   409		Int16:     3,
   410		Int32:     -4,
   411		Int64:     5,
   412		Uint:      6,
   413		Uint8:     7,
   414		Uint16:    8,
   415		Uint32:    9,
   416		Uint64:    10,
   417		Uintptr:   11,
   418		Float32:   13.0,
   419		Float64:   14.0,
   420		String:    "15",
   421		PtrString: &sixteen,
   422	}
   423	
   424	var sixteen = "16"
   425	
   426	const testScalarsInput = `<allscalars>
   427		<True1>true</True1>
   428		<True2>1</True2>
   429		<False1>false</False1>
   430		<False2>0</False2>
   431		<Int>1</Int>
   432		<Int8>-2</Int8>
   433		<Int16>3</Int16>
   434		<Int32>-4</Int32>
   435		<Int64>5</Int64>
   436		<Uint>6</Uint>
   437		<Uint8>7</Uint8>
   438		<Uint16>8</Uint16>
   439		<Uint32>9</Uint32>
   440		<Uint64>10</Uint64>
   441		<Uintptr>11</Uintptr>
   442		<Float>12.0</Float>
   443		<Float32>13.0</Float32>
   444		<Float64>14.0</Float64>
   445		<String>15</String>
   446		<PtrString>16</PtrString>
   447	</allscalars>`
   448	
   449	func TestAllScalars(t *testing.T) {
   450		var a allScalars
   451		err := Unmarshal([]byte(testScalarsInput), &a)
   452	
   453		if err != nil {
   454			t.Fatal(err)
   455		}
   456		if !reflect.DeepEqual(a, all) {
   457			t.Errorf("have %+v want %+v", a, all)
   458		}
   459	}
   460	
   461	type item struct {
   462		Field_a string
   463	}
   464	
   465	func TestIssue569(t *testing.T) {
   466		data := `<item><Field_a>abcd</Field_a></item>`
   467		var i item
   468		err := Unmarshal([]byte(data), &i)
   469	
   470		if err != nil || i.Field_a != "abcd" {
   471			t.Fatal("Expecting abcd")
   472		}
   473	}
   474	
   475	func TestUnquotedAttrs(t *testing.T) {
   476		data := "<tag attr=azAZ09:-_\t>"
   477		d := NewDecoder(strings.NewReader(data))
   478		d.Strict = false
   479		token, err := d.Token()
   480		if _, ok := err.(*SyntaxError); ok {
   481			t.Errorf("Unexpected error: %v", err)
   482		}
   483		if token.(StartElement).Name.Local != "tag" {
   484			t.Errorf("Unexpected tag name: %v", token.(StartElement).Name.Local)
   485		}
   486		attr := token.(StartElement).Attr[0]
   487		if attr.Value != "azAZ09:-_" {
   488			t.Errorf("Unexpected attribute value: %v", attr.Value)
   489		}
   490		if attr.Name.Local != "attr" {
   491			t.Errorf("Unexpected attribute name: %v", attr.Name.Local)
   492		}
   493	}
   494	
   495	func TestValuelessAttrs(t *testing.T) {
   496		tests := [][3]string{
   497			{"<p nowrap>", "p", "nowrap"},
   498			{"<p nowrap >", "p", "nowrap"},
   499			{"<input checked/>", "input", "checked"},
   500			{"<input checked />", "input", "checked"},
   501		}
   502		for _, test := range tests {
   503			d := NewDecoder(strings.NewReader(test[0]))
   504			d.Strict = false
   505			token, err := d.Token()
   506			if _, ok := err.(*SyntaxError); ok {
   507				t.Errorf("Unexpected error: %v", err)
   508			}
   509			if token.(StartElement).Name.Local != test[1] {
   510				t.Errorf("Unexpected tag name: %v", token.(StartElement).Name.Local)
   511			}
   512			attr := token.(StartElement).Attr[0]
   513			if attr.Value != test[2] {
   514				t.Errorf("Unexpected attribute value: %v", attr.Value)
   515			}
   516			if attr.Name.Local != test[2] {
   517				t.Errorf("Unexpected attribute name: %v", attr.Name.Local)
   518			}
   519		}
   520	}
   521	
   522	func TestCopyTokenCharData(t *testing.T) {
   523		data := []byte("same data")
   524		var tok1 Token = CharData(data)
   525		tok2 := CopyToken(tok1)
   526		if !reflect.DeepEqual(tok1, tok2) {
   527			t.Error("CopyToken(CharData) != CharData")
   528		}
   529		data[1] = 'o'
   530		if reflect.DeepEqual(tok1, tok2) {
   531			t.Error("CopyToken(CharData) uses same buffer.")
   532		}
   533	}
   534	
   535	func TestCopyTokenStartElement(t *testing.T) {
   536		elt := StartElement{Name{"", "hello"}, []Attr{{Name{"", "lang"}, "en"}}}
   537		var tok1 Token = elt
   538		tok2 := CopyToken(tok1)
   539		if tok1.(StartElement).Attr[0].Value != "en" {
   540			t.Error("CopyToken overwrote Attr[0]")
   541		}
   542		if !reflect.DeepEqual(tok1, tok2) {
   543			t.Error("CopyToken(StartElement) != StartElement")
   544		}
   545		tok1.(StartElement).Attr[0] = Attr{Name{"", "lang"}, "de"}
   546		if reflect.DeepEqual(tok1, tok2) {
   547			t.Error("CopyToken(CharData) uses same buffer.")
   548		}
   549	}
   550	
   551	func TestSyntaxErrorLineNum(t *testing.T) {
   552		testInput := "<P>Foo<P>\n\n<P>Bar</>\n"
   553		d := NewDecoder(strings.NewReader(testInput))
   554		var err error
   555		for _, err = d.Token(); err == nil; _, err = d.Token() {
   556		}
   557		synerr, ok := err.(*SyntaxError)
   558		if !ok {
   559			t.Error("Expected SyntaxError.")
   560		}
   561		if synerr.Line != 3 {
   562			t.Error("SyntaxError didn't have correct line number.")
   563		}
   564	}
   565	
   566	func TestTrailingRawToken(t *testing.T) {
   567		input := `<FOO></FOO>  `
   568		d := NewDecoder(strings.NewReader(input))
   569		var err error
   570		for _, err = d.RawToken(); err == nil; _, err = d.RawToken() {
   571		}
   572		if err != io.EOF {
   573			t.Fatalf("d.RawToken() = _, %v, want _, io.EOF", err)
   574		}
   575	}
   576	
   577	func TestTrailingToken(t *testing.T) {
   578		input := `<FOO></FOO>  `
   579		d := NewDecoder(strings.NewReader(input))
   580		var err error
   581		for _, err = d.Token(); err == nil; _, err = d.Token() {
   582		}
   583		if err != io.EOF {
   584			t.Fatalf("d.Token() = _, %v, want _, io.EOF", err)
   585		}
   586	}
   587	
   588	func TestEntityInsideCDATA(t *testing.T) {
   589		input := `<test><![CDATA[ &val=foo ]]></test>`
   590		d := NewDecoder(strings.NewReader(input))
   591		var err error
   592		for _, err = d.Token(); err == nil; _, err = d.Token() {
   593		}
   594		if err != io.EOF {
   595			t.Fatalf("d.Token() = _, %v, want _, io.EOF", err)
   596		}
   597	}
   598	
   599	var characterTests = []struct {
   600		in  string
   601		err string
   602	}{
   603		{"\x12<doc/>", "illegal character code U+0012"},
   604		{"<?xml version=\"1.0\"?>\x0b<doc/>", "illegal character code U+000B"},
   605		{"\xef\xbf\xbe<doc/>", "illegal character code U+FFFE"},
   606		{"<?xml version=\"1.0\"?><doc>\r\n<hiya/>\x07<toots/></doc>", "illegal character code U+0007"},
   607		{"<?xml version=\"1.0\"?><doc \x12='value'>what's up</doc>", "expected attribute name in element"},
   608		{"<doc>&abc\x01;</doc>", "invalid character entity &abc (no semicolon)"},
   609		{"<doc>&\x01;</doc>", "invalid character entity & (no semicolon)"},
   610		{"<doc>&\xef\xbf\xbe;</doc>", "invalid character entity &\uFFFE;"},
   611		{"<doc>&hello;</doc>", "invalid character entity &hello;"},
   612	}
   613	
   614	func TestDisallowedCharacters(t *testing.T) {
   615	
   616		for i, tt := range characterTests {
   617			d := NewDecoder(strings.NewReader(tt.in))
   618			var err error
   619	
   620			for err == nil {
   621				_, err = d.Token()
   622			}
   623			synerr, ok := err.(*SyntaxError)
   624			if !ok {
   625				t.Fatalf("input %d d.Token() = _, %v, want _, *SyntaxError", i, err)
   626			}
   627			if synerr.Msg != tt.err {
   628				t.Fatalf("input %d synerr.Msg wrong: want %q, got %q", i, tt.err, synerr.Msg)
   629			}
   630		}
   631	}
   632	
   633	type procInstEncodingTest struct {
   634		expect, got string
   635	}
   636	
   637	var procInstTests = []struct {
   638		input, expect string
   639	}{
   640		{`version="1.0" encoding="utf-8"`, "utf-8"},
   641		{`version="1.0" encoding='utf-8'`, "utf-8"},
   642		{`version="1.0" encoding='utf-8' `, "utf-8"},
   643		{`version="1.0" encoding=utf-8`, ""},
   644		{`encoding="FOO" `, "FOO"},
   645	}
   646	
   647	func TestProcInstEncoding(t *testing.T) {
   648		for _, test := range procInstTests {
   649			got := procInstEncoding(test.input)
   650			if got != test.expect {
   651				t.Errorf("procInstEncoding(%q) = %q; want %q", test.input, got, test.expect)
   652			}
   653		}
   654	}
   655	
   656	// Ensure that directives with comments include the complete
   657	// text of any nested directives.
   658	
   659	var directivesWithCommentsInput = `
   660	<!DOCTYPE [<!-- a comment --><!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]>
   661	<!DOCTYPE [<!ENTITY go "Golang"><!-- a comment-->]>
   662	<!DOCTYPE <!-> <!> <!----> <!-->--> <!--->--> [<!ENTITY go "Golang"><!-- a comment-->]>
   663	`
   664	
   665	var directivesWithCommentsTokens = []Token{
   666		CharData("\n"),
   667		Directive(`DOCTYPE [<!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]`),
   668		CharData("\n"),
   669		Directive(`DOCTYPE [<!ENTITY go "Golang">]`),
   670		CharData("\n"),
   671		Directive(`DOCTYPE <!-> <!>    [<!ENTITY go "Golang">]`),
   672		CharData("\n"),
   673	}
   674	
   675	func TestDirectivesWithComments(t *testing.T) {
   676		d := NewDecoder(strings.NewReader(directivesWithCommentsInput))
   677	
   678		for i, want := range directivesWithCommentsTokens {
   679			have, err := d.Token()
   680			if err != nil {
   681				t.Fatalf("token %d: unexpected error: %s", i, err)
   682			}
   683			if !reflect.DeepEqual(have, want) {
   684				t.Errorf("token %d = %#v want %#v", i, have, want)
   685			}
   686		}
   687	}
   688	
   689	// Writer whose Write method always returns an error.
   690	type errWriter struct{}
   691	
   692	func (errWriter) Write(p []byte) (n int, err error) { return 0, fmt.Errorf("unwritable") }
   693	
   694	func TestEscapeTextIOErrors(t *testing.T) {
   695		expectErr := "unwritable"
   696		err := EscapeText(errWriter{}, []byte{'A'})
   697	
   698		if err == nil || err.Error() != expectErr {
   699			t.Errorf("have %v, want %v", err, expectErr)
   700		}
   701	}
   702	
   703	func TestEscapeTextInvalidChar(t *testing.T) {
   704		input := []byte("A \x00 terminated string.")
   705		expected := "A \uFFFD terminated string."
   706	
   707		buff := new(bytes.Buffer)
   708		if err := EscapeText(buff, input); err != nil {
   709			t.Fatalf("have %v, want nil", err)
   710		}
   711		text := buff.String()
   712	
   713		if text != expected {
   714			t.Errorf("have %v, want %v", text, expected)
   715		}
   716	}

View as plain text