Source file src/pkg/encoding/xml/read_test.go
1
2
3
4
5 package xml
6
7 import (
8 "reflect"
9 "strings"
10 "testing"
11 "time"
12 )
13
14
15
16 func TestUnmarshalFeed(t *testing.T) {
17 var f Feed
18 if err := Unmarshal([]byte(atomFeedString), &f); err != nil {
19 t.Fatalf("Unmarshal: %s", err)
20 }
21 if !reflect.DeepEqual(f, atomFeed) {
22 t.Fatalf("have %#v\nwant %#v", f, atomFeed)
23 }
24 }
25
26
27 const atomFeedString = `
28 <?xml version="1.0" encoding="utf-8"?>
29 <feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-us" updated="2009-10-04T01:35:58+00:00"><title>Code Review - My issues</title><link href="http://codereview.appspot.com/" rel="alternate"></link><link href="http://codereview.appspot.com/rss/mine/rsc" rel="self"></link><id>http://codereview.appspot.com/</id><author><name>rietveld<></name></author><entry><title>rietveld: an attempt at pubsubhubbub
30 </title><link href="http://codereview.appspot.com/126085" rel="alternate"></link><updated>2009-10-04T01:35:58+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:134d9179c41f806be79b3a5f7877d19a</id><summary type="html">
31 An attempt at adding pubsubhubbub support to Rietveld.
32 http://code.google.com/p/pubsubhubbub
33 http://code.google.com/p/rietveld/issues/detail?id=155
34
35 The server side of the protocol is trivial:
36 1. add a &lt;link rel=&quot;hub&quot; href=&quot;hub-server&quot;&gt; tag to all
37 feeds that will be pubsubhubbubbed.
38 2. every time one of those feeds changes, tell the hub
39 with a simple POST request.
40
41 I have tested this by adding debug prints to a local hub
42 server and checking that the server got the right publish
43 requests.
44
45 I can&#39;t quite get the server to work, but I think the bug
46 is not in my code. I think that the server expects to be
47 able to grab the feed and see the feed&#39;s actual URL in
48 the link rel=&quot;self&quot;, but the default value for that drops
49 the :port from the URL, and I cannot for the life of me
50 figure out how to get the Atom generator deep inside
51 django not to do that, or even where it is doing that,
52 or even what code is running to generate the Atom feed.
53 (I thought I knew but I added some assert False statements
54 and it kept running!)
55
56 Ignoring that particular problem, I would appreciate
57 feedback on the right way to get the two values at
58 the top of feeds.py marked NOTE(rsc).
59
60
61 </summary></entry><entry><title>rietveld: correct tab handling
62 </title><link href="http://codereview.appspot.com/124106" rel="alternate"></link><updated>2009-10-03T23:02:17+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:0a2a4f19bb815101f0ba2904aed7c35a</id><summary type="html">
63 This fixes the buggy tab rendering that can be seen at
64 http://codereview.appspot.com/116075/diff/1/2
65
66 The fundamental problem was that the tab code was
67 not being told what column the text began in, so it
68 didn&#39;t know where to put the tab stops. Another problem
69 was that some of the code assumed that string byte
70 offsets were the same as column offsets, which is only
71 true if there are no tabs.
72
73 In the process of fixing this, I cleaned up the arguments
74 to Fold and ExpandTabs and renamed them Break and
75 _ExpandTabs so that I could be sure that I found all the
76 call sites. I also wanted to verify that ExpandTabs was
77 not being used from outside intra_region_diff.py.
78
79
80 </summary></entry></feed> `
81
82 type Feed struct {
83 XMLName Name `xml:"http://www.w3.org/2005/Atom feed"`
84 Title string `xml:"title"`
85 Id string `xml:"id"`
86 Link []Link `xml:"link"`
87 Updated time.Time `xml:"updated,attr"`
88 Author Person `xml:"author"`
89 Entry []Entry `xml:"entry"`
90 }
91
92 type Entry struct {
93 Title string `xml:"title"`
94 Id string `xml:"id"`
95 Link []Link `xml:"link"`
96 Updated time.Time `xml:"updated"`
97 Author Person `xml:"author"`
98 Summary Text `xml:"summary"`
99 }
100
101 type Link struct {
102 Rel string `xml:"rel,attr,omitempty"`
103 Href string `xml:"href,attr"`
104 }
105
106 type Person struct {
107 Name string `xml:"name"`
108 URI string `xml:"uri"`
109 Email string `xml:"email"`
110 InnerXML string `xml:",innerxml"`
111 }
112
113 type Text struct {
114 Type string `xml:"type,attr,omitempty"`
115 Body string `xml:",chardata"`
116 }
117
118 var atomFeed = Feed{
119 XMLName: Name{"http://www.w3.org/2005/Atom", "feed"},
120 Title: "Code Review - My issues",
121 Link: []Link{
122 {Rel: "alternate", Href: "http://codereview.appspot.com/"},
123 {Rel: "self", Href: "http://codereview.appspot.com/rss/mine/rsc"},
124 },
125 Id: "http://codereview.appspot.com/",
126 Updated: ParseTime("2009-10-04T01:35:58+00:00"),
127 Author: Person{
128 Name: "rietveld<>",
129 InnerXML: "<name>rietveld<></name>",
130 },
131 Entry: []Entry{
132 {
133 Title: "rietveld: an attempt at pubsubhubbub\n",
134 Link: []Link{
135 {Rel: "alternate", Href: "http://codereview.appspot.com/126085"},
136 },
137 Updated: ParseTime("2009-10-04T01:35:58+00:00"),
138 Author: Person{
139 Name: "email-address-removed",
140 InnerXML: "<name>email-address-removed</name>",
141 },
142 Id: "urn:md5:134d9179c41f806be79b3a5f7877d19a",
143 Summary: Text{
144 Type: "html",
145 Body: `
146 An attempt at adding pubsubhubbub support to Rietveld.
147 http://code.google.com/p/pubsubhubbub
148 http://code.google.com/p/rietveld/issues/detail?id=155
149
150 The server side of the protocol is trivial:
151 1. add a <link rel="hub" href="hub-server"> tag to all
152 feeds that will be pubsubhubbubbed.
153 2. every time one of those feeds changes, tell the hub
154 with a simple POST request.
155
156 I have tested this by adding debug prints to a local hub
157 server and checking that the server got the right publish
158 requests.
159
160 I can't quite get the server to work, but I think the bug
161 is not in my code. I think that the server expects to be
162 able to grab the feed and see the feed's actual URL in
163 the link rel="self", but the default value for that drops
164 the :port from the URL, and I cannot for the life of me
165 figure out how to get the Atom generator deep inside
166 django not to do that, or even where it is doing that,
167 or even what code is running to generate the Atom feed.
168 (I thought I knew but I added some assert False statements
169 and it kept running!)
170
171 Ignoring that particular problem, I would appreciate
172 feedback on the right way to get the two values at
173 the top of feeds.py marked NOTE(rsc).
174
175
176 `,
177 },
178 },
179 {
180 Title: "rietveld: correct tab handling\n",
181 Link: []Link{
182 {Rel: "alternate", Href: "http://codereview.appspot.com/124106"},
183 },
184 Updated: ParseTime("2009-10-03T23:02:17+00:00"),
185 Author: Person{
186 Name: "email-address-removed",
187 InnerXML: "<name>email-address-removed</name>",
188 },
189 Id: "urn:md5:0a2a4f19bb815101f0ba2904aed7c35a",
190 Summary: Text{
191 Type: "html",
192 Body: `
193 This fixes the buggy tab rendering that can be seen at
194 http://codereview.appspot.com/116075/diff/1/2
195
196 The fundamental problem was that the tab code was
197 not being told what column the text began in, so it
198 didn't know where to put the tab stops. Another problem
199 was that some of the code assumed that string byte
200 offsets were the same as column offsets, which is only
201 true if there are no tabs.
202
203 In the process of fixing this, I cleaned up the arguments
204 to Fold and ExpandTabs and renamed them Break and
205 _ExpandTabs so that I could be sure that I found all the
206 call sites. I also wanted to verify that ExpandTabs was
207 not being used from outside intra_region_diff.py.
208
209
210 `,
211 },
212 },
213 },
214 }
215
216 const pathTestString = `
217 <Result>
218 <Before>1</Before>
219 <Items>
220 <Item1>
221 <Value>A</Value>
222 </Item1>
223 <Item2>
224 <Value>B</Value>
225 </Item2>
226 <Item1>
227 <Value>C</Value>
228 <Value>D</Value>
229 </Item1>
230 <_>
231 <Value>E</Value>
232 </_>
233 </Items>
234 <After>2</After>
235 </Result>
236 `
237
238 type PathTestItem struct {
239 Value string
240 }
241
242 type PathTestA struct {
243 Items []PathTestItem `xml:">Item1"`
244 Before, After string
245 }
246
247 type PathTestB struct {
248 Other []PathTestItem `xml:"Items>Item1"`
249 Before, After string
250 }
251
252 type PathTestC struct {
253 Values1 []string `xml:"Items>Item1>Value"`
254 Values2 []string `xml:"Items>Item2>Value"`
255 Before, After string
256 }
257
258 type PathTestSet struct {
259 Item1 []PathTestItem
260 }
261
262 type PathTestD struct {
263 Other PathTestSet `xml:"Items"`
264 Before, After string
265 }
266
267 type PathTestE struct {
268 Underline string `xml:"Items>_>Value"`
269 Before, After string
270 }
271
272 var pathTests = []interface{}{
273 &PathTestA{Items: []PathTestItem{{"A"}, {"D"}}, Before: "1", After: "2"},
274 &PathTestB{Other: []PathTestItem{{"A"}, {"D"}}, Before: "1", After: "2"},
275 &PathTestC{Values1: []string{"A", "C", "D"}, Values2: []string{"B"}, Before: "1", After: "2"},
276 &PathTestD{Other: PathTestSet{Item1: []PathTestItem{{"A"}, {"D"}}}, Before: "1", After: "2"},
277 &PathTestE{Underline: "E", Before: "1", After: "2"},
278 }
279
280 func TestUnmarshalPaths(t *testing.T) {
281 for _, pt := range pathTests {
282 v := reflect.New(reflect.TypeOf(pt).Elem()).Interface()
283 if err := Unmarshal([]byte(pathTestString), v); err != nil {
284 t.Fatalf("Unmarshal: %s", err)
285 }
286 if !reflect.DeepEqual(v, pt) {
287 t.Fatalf("have %#v\nwant %#v", v, pt)
288 }
289 }
290 }
291
292 type BadPathTestA struct {
293 First string `xml:"items>item1"`
294 Other string `xml:"items>item2"`
295 Second string `xml:"items"`
296 }
297
298 type BadPathTestB struct {
299 Other string `xml:"items>item2>value"`
300 First string `xml:"items>item1"`
301 Second string `xml:"items>item1>value"`
302 }
303
304 type BadPathTestC struct {
305 First string
306 Second string `xml:"First"`
307 }
308
309 type BadPathTestD struct {
310 BadPathEmbeddedA
311 BadPathEmbeddedB
312 }
313
314 type BadPathEmbeddedA struct {
315 First string
316 }
317
318 type BadPathEmbeddedB struct {
319 Second string `xml:"First"`
320 }
321
322 var badPathTests = []struct {
323 v, e interface{}
324 }{
325 {&BadPathTestA{}, &TagPathError{reflect.TypeOf(BadPathTestA{}), "First", "items>item1", "Second", "items"}},
326 {&BadPathTestB{}, &TagPathError{reflect.TypeOf(BadPathTestB{}), "First", "items>item1", "Second", "items>item1>value"}},
327 {&BadPathTestC{}, &TagPathError{reflect.TypeOf(BadPathTestC{}), "First", "", "Second", "First"}},
328 {&BadPathTestD{}, &TagPathError{reflect.TypeOf(BadPathTestD{}), "First", "", "Second", "First"}},
329 }
330
331 func TestUnmarshalBadPaths(t *testing.T) {
332 for _, tt := range badPathTests {
333 err := Unmarshal([]byte(pathTestString), tt.v)
334 if !reflect.DeepEqual(err, tt.e) {
335 t.Fatalf("Unmarshal with %#v didn't fail properly:\nhave %#v,\nwant %#v", tt.v, err, tt.e)
336 }
337 }
338 }
339
340 const OK = "OK"
341 const withoutNameTypeData = `
342 <?xml version="1.0" charset="utf-8"?>
343 <Test3 Attr="OK" />`
344
345 type TestThree struct {
346 XMLName Name `xml:"Test3"`
347 Attr string `xml:",attr"`
348 }
349
350 func TestUnmarshalWithoutNameType(t *testing.T) {
351 var x TestThree
352 if err := Unmarshal([]byte(withoutNameTypeData), &x); err != nil {
353 t.Fatalf("Unmarshal: %s", err)
354 }
355 if x.Attr != OK {
356 t.Fatalf("have %v\nwant %v", x.Attr, OK)
357 }
358 }
359
360 func TestUnmarshalAttr(t *testing.T) {
361 type ParamVal struct {
362 Int int `xml:"int,attr"`
363 }
364
365 type ParamPtr struct {
366 Int *int `xml:"int,attr"`
367 }
368
369 type ParamStringPtr struct {
370 Int *string `xml:"int,attr"`
371 }
372
373 x := []byte(`<Param int="1" />`)
374
375 p1 := &ParamPtr{}
376 if err := Unmarshal(x, p1); err != nil {
377 t.Fatalf("Unmarshal: %s", err)
378 }
379 if p1.Int == nil {
380 t.Fatalf("Unmarshal failed in to *int field")
381 } else if *p1.Int != 1 {
382 t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p1.Int, 1)
383 }
384
385 p2 := &ParamVal{}
386 if err := Unmarshal(x, p2); err != nil {
387 t.Fatalf("Unmarshal: %s", err)
388 }
389 if p2.Int != 1 {
390 t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p2.Int, 1)
391 }
392
393 p3 := &ParamStringPtr{}
394 if err := Unmarshal(x, p3); err != nil {
395 t.Fatalf("Unmarshal: %s", err)
396 }
397 if p3.Int == nil {
398 t.Fatalf("Unmarshal failed in to *string field")
399 } else if *p3.Int != "1" {
400 t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p3.Int, 1)
401 }
402 }
403
404 type Tables struct {
405 HTable string `xml:"http://www.w3.org/TR/html4/ table"`
406 FTable string `xml:"http://www.w3schools.com/furniture table"`
407 }
408
409 var tables = []struct {
410 xml string
411 tab Tables
412 ns string
413 }{
414 {
415 xml: `<Tables>` +
416 `<table xmlns="http://www.w3.org/TR/html4/">hello</table>` +
417 `<table xmlns="http://www.w3schools.com/furniture">world</table>` +
418 `</Tables>`,
419 tab: Tables{"hello", "world"},
420 },
421 {
422 xml: `<Tables>` +
423 `<table xmlns="http://www.w3schools.com/furniture">world</table>` +
424 `<table xmlns="http://www.w3.org/TR/html4/">hello</table>` +
425 `</Tables>`,
426 tab: Tables{"hello", "world"},
427 },
428 {
429 xml: `<Tables xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/">` +
430 `<f:table>world</f:table>` +
431 `<h:table>hello</h:table>` +
432 `</Tables>`,
433 tab: Tables{"hello", "world"},
434 },
435 {
436 xml: `<Tables>` +
437 `<table>bogus</table>` +
438 `</Tables>`,
439 tab: Tables{},
440 },
441 {
442 xml: `<Tables>` +
443 `<table>only</table>` +
444 `</Tables>`,
445 tab: Tables{HTable: "only"},
446 ns: "http://www.w3.org/TR/html4/",
447 },
448 {
449 xml: `<Tables>` +
450 `<table>only</table>` +
451 `</Tables>`,
452 tab: Tables{FTable: "only"},
453 ns: "http://www.w3schools.com/furniture",
454 },
455 {
456 xml: `<Tables>` +
457 `<table>only</table>` +
458 `</Tables>`,
459 tab: Tables{},
460 ns: "something else entirely",
461 },
462 }
463
464 func TestUnmarshalNS(t *testing.T) {
465 for i, tt := range tables {
466 var dst Tables
467 var err error
468 if tt.ns != "" {
469 d := NewDecoder(strings.NewReader(tt.xml))
470 d.DefaultSpace = tt.ns
471 err = d.Decode(&dst)
472 } else {
473 err = Unmarshal([]byte(tt.xml), &dst)
474 }
475 if err != nil {
476 t.Errorf("#%d: Unmarshal: %v", i, err)
477 continue
478 }
479 want := tt.tab
480 if dst != want {
481 t.Errorf("#%d: dst=%+v, want %+v", i, dst, want)
482 }
483 }
484 }
485
486 func TestMarshalNS(t *testing.T) {
487 dst := Tables{"hello", "world"}
488 data, err := Marshal(&dst)
489 if err != nil {
490 t.Fatalf("Marshal: %v", err)
491 }
492 want := `<Tables><table xmlns="http://www.w3.org/TR/html4/">hello</table><table xmlns="http://www.w3schools.com/furniture">world</table></Tables>`
493 str := string(data)
494 if str != want {
495 t.Errorf("have: %q\nwant: %q\n", str, want)
496 }
497 }
498
499 type TableAttrs struct {
500 TAttr TAttr
501 }
502
503 type TAttr struct {
504 HTable string `xml:"http://www.w3.org/TR/html4/ table,attr"`
505 FTable string `xml:"http://www.w3schools.com/furniture table,attr"`
506 Lang string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"`
507 Other1 string `xml:"http://golang.org/xml/ other,attr,omitempty"`
508 Other2 string `xml:"http://golang.org/xmlfoo/ other,attr,omitempty"`
509 Other3 string `xml:"http://golang.org/json/ other,attr,omitempty"`
510 Other4 string `xml:"http://golang.org/2/json/ other,attr,omitempty"`
511 }
512
513 var tableAttrs = []struct {
514 xml string
515 tab TableAttrs
516 ns string
517 }{
518 {
519 xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` +
520 `h:table="hello" f:table="world" ` +
521 `/></TableAttrs>`,
522 tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}},
523 },
524 {
525 xml: `<TableAttrs><TAttr xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/" ` +
526 `h:table="hello" f:table="world" ` +
527 `/></TableAttrs>`,
528 tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}},
529 },
530 {
531 xml: `<TableAttrs><TAttr ` +
532 `h:table="hello" f:table="world" xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/" ` +
533 `/></TableAttrs>`,
534 tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}},
535 },
536 {
537
538 xml: `<TableAttrs xmlns="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` +
539 `h:table="hello" table="world" ` +
540 `/></TableAttrs>`,
541 tab: TableAttrs{TAttr{HTable: "hello", FTable: ""}},
542 },
543 {
544
545 xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture"><TAttr xmlns="http://www.w3.org/TR/html4/" ` +
546 `table="hello" f:table="world" ` +
547 `/></TableAttrs>`,
548 tab: TableAttrs{TAttr{HTable: "", FTable: "world"}},
549 },
550 {
551 xml: `<TableAttrs><TAttr ` +
552 `table="bogus" ` +
553 `/></TableAttrs>`,
554 tab: TableAttrs{},
555 },
556 {
557
558 xml: `<TableAttrs xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` +
559 `h:table="hello" table="world" ` +
560 `/></TableAttrs>`,
561 tab: TableAttrs{TAttr{HTable: "hello", FTable: ""}},
562 ns: "http://www.w3schools.com/furniture",
563 },
564 {
565
566 xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture"><TAttr ` +
567 `table="hello" f:table="world" ` +
568 `/></TableAttrs>`,
569 tab: TableAttrs{TAttr{HTable: "", FTable: "world"}},
570 ns: "http://www.w3.org/TR/html4/",
571 },
572 {
573 xml: `<TableAttrs><TAttr ` +
574 `table="bogus" ` +
575 `/></TableAttrs>`,
576 tab: TableAttrs{},
577 ns: "something else entirely",
578 },
579 }
580
581 func TestUnmarshalNSAttr(t *testing.T) {
582 for i, tt := range tableAttrs {
583 var dst TableAttrs
584 var err error
585 if tt.ns != "" {
586 d := NewDecoder(strings.NewReader(tt.xml))
587 d.DefaultSpace = tt.ns
588 err = d.Decode(&dst)
589 } else {
590 err = Unmarshal([]byte(tt.xml), &dst)
591 }
592 if err != nil {
593 t.Errorf("#%d: Unmarshal: %v", i, err)
594 continue
595 }
596 want := tt.tab
597 if dst != want {
598 t.Errorf("#%d: dst=%+v, want %+v", i, dst, want)
599 }
600 }
601 }
602
603 func TestMarshalNSAttr(t *testing.T) {
604 src := TableAttrs{TAttr{"hello", "world", "en_US", "other1", "other2", "other3", "other4"}}
605 data, err := Marshal(&src)
606 if err != nil {
607 t.Fatalf("Marshal: %v", err)
608 }
609 want := `<TableAttrs><TAttr xmlns:html4="http://www.w3.org/TR/html4/" html4:table="hello" xmlns:furniture="http://www.w3schools.com/furniture" furniture:table="world" xml:lang="en_US" xmlns:_xml="http://golang.org/xml/" _xml:other="other1" xmlns:_xmlfoo="http://golang.org/xmlfoo/" _xmlfoo:other="other2" xmlns:json="http://golang.org/json/" json:other="other3" xmlns:json_1="http://golang.org/2/json/" json_1:other="other4"></TAttr></TableAttrs>`
610 str := string(data)
611 if str != want {
612 t.Errorf("Marshal:\nhave: %#q\nwant: %#q\n", str, want)
613 }
614
615 var dst TableAttrs
616 if err := Unmarshal(data, &dst); err != nil {
617 t.Errorf("Unmarshal: %v", err)
618 }
619
620 if dst != src {
621 t.Errorf("Unmarshal = %q, want %q", dst, src)
622 }
623 }
View as plain text