Source file src/pkg/encoding/xml/xml_test.go
1
2
3
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 <>'" 白鵬翔</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
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
149 "<>",
150 "<t/a",
151 "<0 />",
152 "<?0 >",
153
154 "</0>",
155 "<t 0=''>",
156 "<t a='&'>",
157 "<t a='<'>",
158 "<t> c;</t>",
159 "<t a>",
160 "<t a=>",
161 "<t a=v>",
162
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>{</tag>
179 <tag>&#zzz;</tag>
180 <tag>&なまえ3;</tag>
181 <tag><-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("{"),
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("<-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
308
309
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
657
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
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