1// Copyright 2022 The Bazel Authors. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package resxml 16 17import ( 18 "bytes" 19 "context" 20 "encoding/xml" 21 "io" 22 "reflect" 23 "testing" 24 25 "src/tools/ak/res/respipe/respipe" 26) 27 28const ( 29 doc = ` 30 <Person> 31 <FullName>Grace R. Emlin</FullName> 32 <Company>Example Inc.</Company> 33 <Email where="home"> 34 <Addr>gre@example.com</Addr> 35 </Email> 36 <City>Hanga Rao<Street>1234 Main St.</Street>RandomText</City> 37 <Email where='work'> 38 <Addr>gre@work.com</Addr> 39 </Email> 40 <Group> 41 <Value>Friends</Value> 42 <Value>Squash</Value> 43 </Group> 44 <State>Easter Island</State> 45 </Person> 46 ` 47) 48 49func TestForwardChildren(t *testing.T) { 50 ctx, cancel := context.WithCancel(respipe.PrefixErr(context.Background(), "test doc: ")) 51 defer cancel() 52 xmlC, errC := StreamDoc(ctx, bytes.NewBufferString(doc)) 53 xe, ok := ConsumeUntil(xml.Name{Local: "City"}, xmlC) 54 if !ok { 55 t.Fatalf("Expected to find: %s in %s", xml.Name{Local: "City"}, doc) 56 } 57 childC := ForwardChildren(ctx, xe, xmlC) 58 wantEvents := []XMLEvent{ 59 { 60 Token: xml.CharData("Hanga Rao"), 61 }, 62 { 63 Token: xml.StartElement{Name: xml.Name{Local: "Street"}, Attr: []xml.Attr{}}, 64 }, 65 { 66 Token: xml.CharData("1234 Main St."), 67 }, 68 { 69 Token: xml.EndElement{Name: xml.Name{Local: "Street"}}, 70 }, 71 { 72 Token: xml.CharData("RandomText"), 73 }, 74 } 75 var gotEvents []XMLEvent 76 for childC != nil || errC != nil { 77 select { 78 case xe, ok := <-childC: 79 if !ok { 80 childC = nil 81 cancel() 82 continue 83 } 84 xe.Offset = 0 85 gotEvents = append(gotEvents, xe) 86 case e, ok := <-errC: 87 if !ok { 88 errC = nil 89 continue 90 } 91 t.Errorf("unexpected error: %v", e) 92 } 93 } 94 95 if !reflect.DeepEqual(wantEvents, gotEvents) { 96 t.Errorf("Got children: %#v wanted: %#v", gotEvents, wantEvents) 97 } 98 99} 100 101func TestAttrs(t *testing.T) { 102 tests := []struct { 103 arg XMLEvent 104 want []xml.Attr 105 }{ 106 { 107 XMLEvent{ 108 Token: xml.StartElement{ 109 Attr: []xml.Attr{ 110 { 111 Name: xml.Name{Local: "dog"}, 112 Value: "shepard", 113 }, 114 { 115 Name: xml.Name{Local: "cat"}, 116 Value: "cheshire", 117 }, 118 }, 119 }, 120 }, 121 []xml.Attr{ 122 { 123 Name: xml.Name{Local: "dog"}, 124 Value: "shepard", 125 }, 126 { 127 Name: xml.Name{Local: "cat"}, 128 Value: "cheshire", 129 }, 130 }, 131 }, 132 { 133 XMLEvent{Token: xml.StartElement{}}, 134 []xml.Attr(nil), 135 }, 136 { 137 XMLEvent{Token: xml.CharData("foo")}, 138 []xml.Attr(nil), 139 }, 140 } 141 142 for _, tc := range tests { 143 got := Attrs(tc.arg) 144 if !reflect.DeepEqual(got, tc.want) { 145 t.Errorf("Attrs(%#v): %#v wanted %#v", tc.arg, got, tc.want) 146 } 147 } 148} 149 150func TestConsumeUntil(t *testing.T) { 151 ctx, cancel := context.WithCancel(respipe.PrefixErr(context.Background(), "test doc: ")) 152 defer cancel() 153 xmlC, errC := StreamDoc(ctx, bytes.NewBufferString(doc)) 154 155 xe, ok := ConsumeUntil(xml.Name{Local: "Email"}, xmlC) 156 if !ok { 157 t.Fatalf("Expected to find: %s in %s", xml.Name{Local: "Email"}, doc) 158 } 159 if se, ok := xe.Token.(xml.StartElement); ok { 160 want := []xml.Attr{{xml.Name{Local: "where"}, "home"}} 161 if !reflect.DeepEqual(want, se.Attr) { 162 t.Errorf("Got attr: %v wanted: %v", se.Attr, want) 163 } 164 } else { 165 t.Fatalf("Got: %v Expected to stop on a start element", xe) 166 } 167 xe, ok = ConsumeUntil(xml.Name{Local: "Email"}, xmlC) 168 if !ok { 169 t.Fatalf("Expected to find: %s in %s", xml.Name{Local: "Email"}, doc) 170 } 171 if se, ok := xe.Token.(xml.StartElement); ok { 172 want := []xml.Attr{{xml.Name{Local: "where"}, "work"}} 173 if !reflect.DeepEqual(want, se.Attr) { 174 t.Errorf("Got attr: %v wanted: %v", se.Attr, want) 175 } 176 } else { 177 t.Fatalf("Got: %v Expected to stop on a start element", xe) 178 } 179 xe, ok = ConsumeUntil(xml.Name{Local: "Email"}, xmlC) 180 if ok { 181 t.Fatalf("Expected no more nodes with: %v got: %v in doc: %s", xml.Name{Local: "Email"}, xe, doc) 182 } 183 e, ok := <-errC 184 if ok { 185 t.Fatalf("Expected no errors during parse: %v", e) 186 } 187} 188 189func TestStreamDoc(t *testing.T) { 190 dec := xml.NewDecoder(bytes.NewBufferString(doc)) 191 var events []XMLEvent 192 for { 193 tok, err := dec.Token() 194 if err == io.EOF { 195 break 196 } 197 if err != nil { 198 t.Fatalf("Unexpected xml parse failure: %v", err) 199 } 200 events = append(events, XMLEvent{xml.CopyToken(tok), dec.InputOffset()}) 201 } 202 ctx, cancel := context.WithCancel(respipe.PrefixErr(context.Background(), "test doc: ")) 203 defer cancel() 204 xmlC, errC := StreamDoc(ctx, bytes.NewBufferString(doc)) 205 var got []XMLEvent 206 for xmlC != nil || errC != nil { 207 select { 208 case e, ok := <-errC: 209 if !ok { 210 errC = nil 211 continue 212 } 213 t.Errorf("Unexpected error: %v", e) 214 case xe, ok := <-xmlC: 215 if !ok { 216 xmlC = nil 217 continue 218 } 219 got = append(got, xe) 220 } 221 } 222 if !reflect.DeepEqual(events, got) { 223 t.Errorf("StreamDoc() got: %v wanted: %v", got, events) 224 } 225 226} 227