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.
17 // Stripped down Atom feed data structures.
19 func TestUnmarshalFeed(t *testing.T) {
21 if err := Unmarshal([]byte(atomFeedString), &f); err != nil {
22 t.Fatalf("Unmarshal: %s", err)
24 if !reflect.DeepEqual(f, atomFeed) {
25 t.Fatalf("have %#v\nwant %#v", f, atomFeed)
29 // hget http://codereview.appspot.com/rss/mine/rsc
30 const atomFeedString = `
31 <?xml version="1.0" encoding="utf-8"?>
32 <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
33 </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">
34 An attempt at adding pubsubhubbub support to Rietveld.
35 http://code.google.com/p/pubsubhubbub
36 http://code.google.com/p/rietveld/issues/detail?id=155
38 The server side of the protocol is trivial:
39 1. add a &lt;link rel=&quot;hub&quot; href=&quot;hub-server&quot;&gt; tag to all
40 feeds that will be pubsubhubbubbed.
41 2. every time one of those feeds changes, tell the hub
42 with a simple POST request.
44 I have tested this by adding debug prints to a local hub
45 server and checking that the server got the right publish
48 I can&#39;t quite get the server to work, but I think the bug
49 is not in my code. I think that the server expects to be
50 able to grab the feed and see the feed&#39;s actual URL in
51 the link rel=&quot;self&quot;, but the default value for that drops
52 the :port from the URL, and I cannot for the life of me
53 figure out how to get the Atom generator deep inside
54 django not to do that, or even where it is doing that,
55 or even what code is running to generate the Atom feed.
56 (I thought I knew but I added some assert False statements
59 Ignoring that particular problem, I would appreciate
60 feedback on the right way to get the two values at
61 the top of feeds.py marked NOTE(rsc).
64 </summary></entry><entry><title>rietveld: correct tab handling
65 </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">
66 This fixes the buggy tab rendering that can be seen at
67 http://codereview.appspot.com/116075/diff/1/2
69 The fundamental problem was that the tab code was
70 not being told what column the text began in, so it
71 didn&#39;t know where to put the tab stops. Another problem
72 was that some of the code assumed that string byte
73 offsets were the same as column offsets, which is only
74 true if there are no tabs.
76 In the process of fixing this, I cleaned up the arguments
77 to Fold and ExpandTabs and renamed them Break and
78 _ExpandTabs so that I could be sure that I found all the
79 call sites. I also wanted to verify that ExpandTabs was
80 not being used from outside intra_region_diff.py.
83 </summary></entry></feed> `
86 XMLName Name `xml:"http://www.w3.org/2005/Atom feed"`
87 Title string `xml:"title"`
89 Link []Link `xml:"link"`
90 Updated time.Time `xml:"updated,attr"`
91 Author Person `xml:"author"`
92 Entry []Entry `xml:"entry"`
96 Title string `xml:"title"`
98 Link []Link `xml:"link"`
99 Updated time.Time `xml:"updated"`
100 Author Person `xml:"author"`
101 Summary Text `xml:"summary"`
105 Rel string `xml:"rel,attr,omitempty"`
106 Href string `xml:"href,attr"`
110 Name string `xml:"name"`
111 URI string `xml:"uri"`
112 Email string `xml:"email"`
113 InnerXML string `xml:",innerxml"`
117 Type string `xml:"type,attr,omitempty"`
118 Body string `xml:",chardata"`
122 XMLName: Name{"http://www.w3.org/2005/Atom", "feed"},
123 Title: "Code Review - My issues",
125 {Rel: "alternate", Href: "http://codereview.appspot.com/"},
126 {Rel: "self", Href: "http://codereview.appspot.com/rss/mine/rsc"},
128 Id: "http://codereview.appspot.com/",
129 Updated: ParseTime("2009-10-04T01:35:58+00:00"),
132 InnerXML: "<name>rietveld<></name>",
136 Title: "rietveld: an attempt at pubsubhubbub\n",
138 {Rel: "alternate", Href: "http://codereview.appspot.com/126085"},
140 Updated: ParseTime("2009-10-04T01:35:58+00:00"),
142 Name: "email-address-removed",
143 InnerXML: "<name>email-address-removed</name>",
145 Id: "urn:md5:134d9179c41f806be79b3a5f7877d19a",
149 An attempt at adding pubsubhubbub support to Rietveld.
150 http://code.google.com/p/pubsubhubbub
151 http://code.google.com/p/rietveld/issues/detail?id=155
153 The server side of the protocol is trivial:
154 1. add a <link rel="hub" href="hub-server"> tag to all
155 feeds that will be pubsubhubbubbed.
156 2. every time one of those feeds changes, tell the hub
157 with a simple POST request.
159 I have tested this by adding debug prints to a local hub
160 server and checking that the server got the right publish
163 I can't quite get the server to work, but I think the bug
164 is not in my code. I think that the server expects to be
165 able to grab the feed and see the feed's actual URL in
166 the link rel="self", but the default value for that drops
167 the :port from the URL, and I cannot for the life of me
168 figure out how to get the Atom generator deep inside
169 django not to do that, or even where it is doing that,
170 or even what code is running to generate the Atom feed.
171 (I thought I knew but I added some assert False statements
172 and it kept running!)
174 Ignoring that particular problem, I would appreciate
175 feedback on the right way to get the two values at
176 the top of feeds.py marked NOTE(rsc).
183 Title: "rietveld: correct tab handling\n",
185 {Rel: "alternate", Href: "http://codereview.appspot.com/124106"},
187 Updated: ParseTime("2009-10-03T23:02:17+00:00"),
189 Name: "email-address-removed",
190 InnerXML: "<name>email-address-removed</name>",
192 Id: "urn:md5:0a2a4f19bb815101f0ba2904aed7c35a",
196 This fixes the buggy tab rendering that can be seen at
197 http://codereview.appspot.com/116075/diff/1/2
199 The fundamental problem was that the tab code was
200 not being told what column the text began in, so it
201 didn't know where to put the tab stops. Another problem
202 was that some of the code assumed that string byte
203 offsets were the same as column offsets, which is only
204 true if there are no tabs.
206 In the process of fixing this, I cleaned up the arguments
207 to Fold and ExpandTabs and renamed them Break and
208 _ExpandTabs so that I could be sure that I found all the
209 call sites. I also wanted to verify that ExpandTabs was
210 not being used from outside intra_region_diff.py.
219 const pathTestString = `
241 type PathTestItem struct {
245 type PathTestA struct {
246 Items []PathTestItem `xml:">Item1"`
250 type PathTestB struct {
251 Other []PathTestItem `xml:"Items>Item1"`
255 type PathTestC struct {
256 Values1 []string `xml:"Items>Item1>Value"`
257 Values2 []string `xml:"Items>Item2>Value"`
261 type PathTestSet struct {
265 type PathTestD struct {
266 Other PathTestSet `xml:"Items"`
270 type PathTestE struct {
271 Underline string `xml:"Items>_>Value"`
275 var pathTests = []interface{}{
276 &PathTestA{Items: []PathTestItem{{"A"}, {"D"}}, Before: "1", After: "2"},
277 &PathTestB{Other: []PathTestItem{{"A"}, {"D"}}, Before: "1", After: "2"},
278 &PathTestC{Values1: []string{"A", "C", "D"}, Values2: []string{"B"}, Before: "1", After: "2"},
279 &PathTestD{Other: PathTestSet{Item1: []PathTestItem{{"A"}, {"D"}}}, Before: "1", After: "2"},
280 &PathTestE{Underline: "E", Before: "1", After: "2"},
283 func TestUnmarshalPaths(t *testing.T) {
284 for _, pt := range pathTests {
285 v := reflect.New(reflect.TypeOf(pt).Elem()).Interface()
286 if err := Unmarshal([]byte(pathTestString), v); err != nil {
287 t.Fatalf("Unmarshal: %s", err)
289 if !reflect.DeepEqual(v, pt) {
290 t.Fatalf("have %#v\nwant %#v", v, pt)
295 type BadPathTestA struct {
296 First string `xml:"items>item1"`
297 Other string `xml:"items>item2"`
298 Second string `xml:"items"`
301 type BadPathTestB struct {
302 Other string `xml:"items>item2>value"`
303 First string `xml:"items>item1"`
304 Second string `xml:"items>item1>value"`
307 type BadPathTestC struct {
309 Second string `xml:"First"`
312 type BadPathTestD struct {
317 type BadPathEmbeddedA struct {
321 type BadPathEmbeddedB struct {
322 Second string `xml:"First"`
325 var badPathTests = []struct {
328 {&BadPathTestA{}, &TagPathError{reflect.TypeOf(BadPathTestA{}), "First", "items>item1", "Second", "items"}},
329 {&BadPathTestB{}, &TagPathError{reflect.TypeOf(BadPathTestB{}), "First", "items>item1", "Second", "items>item1>value"}},
330 {&BadPathTestC{}, &TagPathError{reflect.TypeOf(BadPathTestC{}), "First", "", "Second", "First"}},
331 {&BadPathTestD{}, &TagPathError{reflect.TypeOf(BadPathTestD{}), "First", "", "Second", "First"}},
334 func TestUnmarshalBadPaths(t *testing.T) {
335 for _, tt := range badPathTests {
336 err := Unmarshal([]byte(pathTestString), tt.v)
337 if !reflect.DeepEqual(err, tt.e) {
338 t.Fatalf("Unmarshal with %#v didn't fail properly:\nhave %#v,\nwant %#v", tt.v, err, tt.e)
344 const withoutNameTypeData = `
345 <?xml version="1.0" charset="utf-8"?>
348 type TestThree struct {
349 XMLName Name `xml:"Test3"`
350 Attr string `xml:",attr"`
353 func TestUnmarshalWithoutNameType(t *testing.T) {
355 if err := Unmarshal([]byte(withoutNameTypeData), &x); err != nil {
356 t.Fatalf("Unmarshal: %s", err)
359 t.Fatalf("have %v\nwant %v", x.Attr, OK)
363 func TestUnmarshalAttr(t *testing.T) {
364 type ParamVal struct {
365 Int int `xml:"int,attr"`
368 type ParamPtr struct {
369 Int *int `xml:"int,attr"`
372 type ParamStringPtr struct {
373 Int *string `xml:"int,attr"`
376 x := []byte(`<Param int="1" />`)
379 if err := Unmarshal(x, p1); err != nil {
380 t.Fatalf("Unmarshal: %s", err)
383 t.Fatalf("Unmarshal failed in to *int field")
384 } else if *p1.Int != 1 {
385 t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p1.Int, 1)
389 if err := Unmarshal(x, p2); err != nil {
390 t.Fatalf("Unmarshal: %s", err)
393 t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p2.Int, 1)
396 p3 := &ParamStringPtr{}
397 if err := Unmarshal(x, p3); err != nil {
398 t.Fatalf("Unmarshal: %s", err)
401 t.Fatalf("Unmarshal failed in to *string field")
402 } else if *p3.Int != "1" {
403 t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p3.Int, 1)
408 HTable string `xml:"http://www.w3.org/TR/html4/ table"`
409 FTable string `xml:"http://www.w3schools.com/furniture table"`
412 var tables = []struct {
419 `<table xmlns="http://www.w3.org/TR/html4/">hello</table>` +
420 `<table xmlns="http://www.w3schools.com/furniture">world</table>` +
422 tab: Tables{"hello", "world"},
426 `<table xmlns="http://www.w3schools.com/furniture">world</table>` +
427 `<table xmlns="http://www.w3.org/TR/html4/">hello</table>` +
429 tab: Tables{"hello", "world"},
432 xml: `<Tables xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/">` +
433 `<f:table>world</f:table>` +
434 `<h:table>hello</h:table>` +
436 tab: Tables{"hello", "world"},
440 `<table>bogus</table>` +
446 `<table>only</table>` +
448 tab: Tables{HTable: "only"},
449 ns: "http://www.w3.org/TR/html4/",
453 `<table>only</table>` +
455 tab: Tables{FTable: "only"},
456 ns: "http://www.w3schools.com/furniture",
460 `<table>only</table>` +
463 ns: "something else entirely",
467 func TestUnmarshalNS(t *testing.T) {
468 for i, tt := range tables {
472 d := NewDecoder(strings.NewReader(tt.xml))
473 d.DefaultSpace = tt.ns
476 err = Unmarshal([]byte(tt.xml), &dst)
479 t.Errorf("#%d: Unmarshal: %v", i, err)
484 t.Errorf("#%d: dst=%+v, want %+v", i, dst, want)
489 func TestRoundTrip(t *testing.T) {
491 const s = `<ex:element xmlns:ex="http://example.com/schema"></ex:element>`
492 in := bytes.NewBufferString(s)
493 for i := 0; i < 10; i++ {
494 out := &bytes.Buffer{}
504 fmt.Println("failed:", err)
512 if got := in.String(); got != s {
513 t.Errorf("have: %q\nwant: %q\n", got, s)
517 func TestMarshalNS(t *testing.T) {
518 dst := Tables{"hello", "world"}
519 data, err := Marshal(&dst)
521 t.Fatalf("Marshal: %v", err)
523 want := `<Tables><table xmlns="http://www.w3.org/TR/html4/">hello</table><table xmlns="http://www.w3schools.com/furniture">world</table></Tables>`
526 t.Errorf("have: %q\nwant: %q\n", str, want)
530 type TableAttrs struct {
535 HTable string `xml:"http://www.w3.org/TR/html4/ table,attr"`
536 FTable string `xml:"http://www.w3schools.com/furniture table,attr"`
537 Lang string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"`
538 Other1 string `xml:"http://golang.org/xml/ other,attr,omitempty"`
539 Other2 string `xml:"http://golang.org/xmlfoo/ other,attr,omitempty"`
540 Other3 string `xml:"http://golang.org/json/ other,attr,omitempty"`
541 Other4 string `xml:"http://golang.org/2/json/ other,attr,omitempty"`
544 var tableAttrs = []struct {
550 xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` +
551 `h:table="hello" f:table="world" ` +
553 tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}},
556 xml: `<TableAttrs><TAttr xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/" ` +
557 `h:table="hello" f:table="world" ` +
559 tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}},
562 xml: `<TableAttrs><TAttr ` +
563 `h:table="hello" f:table="world" xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/" ` +
565 tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}},
568 // Default space does not apply to attribute names.
569 xml: `<TableAttrs xmlns="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` +
570 `h:table="hello" table="world" ` +
572 tab: TableAttrs{TAttr{HTable: "hello", FTable: ""}},
575 // Default space does not apply to attribute names.
576 xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture"><TAttr xmlns="http://www.w3.org/TR/html4/" ` +
577 `table="hello" f:table="world" ` +
579 tab: TableAttrs{TAttr{HTable: "", FTable: "world"}},
582 xml: `<TableAttrs><TAttr ` +
588 // Default space does not apply to attribute names.
589 xml: `<TableAttrs xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` +
590 `h:table="hello" table="world" ` +
592 tab: TableAttrs{TAttr{HTable: "hello", FTable: ""}},
593 ns: "http://www.w3schools.com/furniture",
596 // Default space does not apply to attribute names.
597 xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture"><TAttr ` +
598 `table="hello" f:table="world" ` +
600 tab: TableAttrs{TAttr{HTable: "", FTable: "world"}},
601 ns: "http://www.w3.org/TR/html4/",
604 xml: `<TableAttrs><TAttr ` +
608 ns: "something else entirely",
612 func TestUnmarshalNSAttr(t *testing.T) {
613 for i, tt := range tableAttrs {
617 d := NewDecoder(strings.NewReader(tt.xml))
618 d.DefaultSpace = tt.ns
621 err = Unmarshal([]byte(tt.xml), &dst)
624 t.Errorf("#%d: Unmarshal: %v", i, err)
629 t.Errorf("#%d: dst=%+v, want %+v", i, dst, want)
634 func TestMarshalNSAttr(t *testing.T) {
635 src := TableAttrs{TAttr{"hello", "world", "en_US", "other1", "other2", "other3", "other4"}}
636 data, err := Marshal(&src)
638 t.Fatalf("Marshal: %v", err)
640 want := `<TableAttrs><TAttr xmlns:json_1="http://golang.org/2/json/" xmlns:json="http://golang.org/json/" xmlns:_xmlfoo="http://golang.org/xmlfoo/" xmlns:_xml="http://golang.org/xml/" xmlns:furniture="http://www.w3schools.com/furniture" xmlns:html4="http://www.w3.org/TR/html4/" html4:table="hello" furniture:table="world" xml:lang="en_US" _xml:other="other1" _xmlfoo:other="other2" json:other="other3" json_1:other="other4"></TAttr></TableAttrs>`
643 t.Errorf("Marshal:\nhave: %#q\nwant: %#q\n", str, want)
647 if err := Unmarshal(data, &dst); err != nil {
648 t.Errorf("Unmarshal: %v", err)
652 t.Errorf("Unmarshal = %q, want %q", dst, src)
656 type MyCharData struct {
660 func (m *MyCharData) UnmarshalXML(d *Decoder, start StartElement) error {
663 if err == io.EOF { // found end of element
669 if char, ok := t.(CharData); ok {
670 m.body += string(char)
676 var _ Unmarshaler = (*MyCharData)(nil)
678 func (m *MyCharData) UnmarshalXMLAttr(attr Attr) error {
679 panic("must not call")
686 func (m *MyAttr) UnmarshalXMLAttr(attr Attr) error {
691 var _ UnmarshalerAttr = (*MyAttr)(nil)
693 type MyStruct struct {
695 Attr *MyAttr `xml:",attr"`
698 Attr2 MyAttr `xml:",attr"`
701 func TestUnmarshaler(t *testing.T) {
702 xml := `<?xml version="1.0" encoding="utf-8"?>
703 <MyStruct Attr="attr1" Attr2="attr2">
704 <Data>hello <!-- comment -->world</Data>
705 <Data2>howdy <!-- comment -->world</Data2>
710 if err := Unmarshal([]byte(xml), &m); err != nil {
714 if m.Data == nil || m.Attr == nil || m.Data.body != "hello world" || m.Attr.attr != "attr1" || m.Data2.body != "howdy world" || m.Attr2.attr != "attr2" {
715 t.Errorf("m=%#+v\n", m)
724 Pea interface{} `xml:"Pea"`
727 // https://golang.org/issue/6836
728 func TestUnmarshalIntoInterface(t *testing.T) {
731 xml := `<Pod><Pea><Cotelydon>Green stuff</Cotelydon></Pea></Pod>`
732 err := Unmarshal([]byte(xml), pod)
734 t.Fatalf("failed to unmarshal %q: %v", xml, err)
736 pea, ok := pod.Pea.(*Pea)
738 t.Fatalf("unmarshalled into wrong type: have %T want *Pea", pod.Pea)
740 have, want := pea.Cotelydon, "Green stuff"
742 t.Errorf("failed to unmarshal into interface, have %q want %q", have, want)