xref: /aosp_15_r20/external/bazelbuild-rules_android/src/tools/ak/res/resxml/xml_parser_test.go (revision 9e965d6fece27a77de5377433c2f7e6999b8cc0b)
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