1 package ast_test
2
3 import (
4 "testing"
5
6 "github.com/google/go-cmp/cmp"
7 "github.com/kylelemons/godebug/diff"
8 "github.com/protocolbuffers/txtpbfmt/ast"
9 "github.com/protocolbuffers/txtpbfmt/parser"
10 )
11
12 func TestChainNodeLess(t *testing.T) {
13 byFirstChar := func(_, ni, nj *ast.Node, isWholeSlice bool) bool {
14 return ni.Name[0] < nj.Name[0]
15 }
16 bySecondChar := func(_, ni, nj *ast.Node, isWholeSlice bool) bool {
17 return ni.Name[1] < nj.Name[1]
18 }
19 tests := []struct {
20 name string
21 a ast.NodeLess
22 b ast.NodeLess
23 names []string
24 want []string
25 }{{
26 name: "nil + byFirstChar",
27 a: nil,
28 b: byFirstChar,
29 names: []string{"c", "b", "z", "a"},
30 want: []string{"a", "b", "c", "z"},
31 }, {
32 name: "byFirstChar + nil",
33 a: nil,
34 b: byFirstChar,
35 names: []string{"c", "b", "z", "a"},
36 want: []string{"a", "b", "c", "z"},
37 }, {
38 name: "byFirstChar + bySecondChar",
39 a: byFirstChar,
40 b: bySecondChar,
41 names: []string{"zc", "bb", "za", "aa", "ac", "ba", "bc", "ab", "zb"},
42 want: []string{"aa", "ab", "ac", "ba", "bb", "bc", "za", "zb", "zc"},
43 }, {
44 name: "bySecondChar + byFirstChar",
45 a: bySecondChar,
46 b: byFirstChar,
47 names: []string{"zc", "bb", "za", "aa", "ac", "ba", "bc", "ab", "zb"},
48 want: []string{"aa", "ba", "za", "ab", "bb", "zb", "ac", "bc", "zc"},
49 }}
50
51 sortNames := func(names []string, less ast.NodeLess) []string {
52 ns := []*ast.Node{}
53 for _, n := range names {
54 ns = append(ns, &ast.Node{Name: n})
55 }
56 ast.SortNodes(nil , ns, less)
57 rs := []string{}
58 for _, n := range ns {
59 rs = append(rs, n.Name)
60 }
61 return rs
62 }
63 for _, tc := range tests {
64 less := ast.ChainNodeLess(tc.a, tc.b)
65 got := sortNames(tc.names, less)
66 if diff := cmp.Diff(tc.want, got); diff != "" {
67 t.Errorf("%s sorting %v returned diff (-want, +got):\n%s", tc.name, tc.names, diff)
68 }
69 }
70 }
71
72 func TestGetFromPath(t *testing.T) {
73 content := `first {
74 second {
75 third: "v1"
76 third: "v2"
77 }
78 second {
79 third: "v3"
80 third: "v4"
81 }
82 }
83 first {
84 second {
85 third: "v5"
86 third: "v6"
87 }
88 second {
89 third: "v7"
90 third: "v8"
91 }
92 }
93 `
94 inputs := []struct {
95 in string
96 path []string
97 want string
98 }{{
99 in: content,
100 path: nil,
101 want: ``,
102 }, {
103 in: content,
104 path: []string{"first", "second", "third"},
105 want: `third: "v1"
106 third: "v2"
107 third: "v3"
108 third: "v4"
109 third: "v5"
110 third: "v6"
111 third: "v7"
112 third: "v8"
113 `,
114 }, {
115 in: content,
116 path: []string{"first", "second"},
117 want: `second {
118 third: "v1"
119 third: "v2"
120 }
121 second {
122 third: "v3"
123 third: "v4"
124 }
125 second {
126 third: "v5"
127 third: "v6"
128 }
129 second {
130 third: "v7"
131 third: "v8"
132 }
133 `,
134 }, {
135 in: content,
136 path: []string{"first"},
137 want: content,
138 }}
139 for _, input := range inputs {
140 nodes, err := parser.Parse([]byte(input.in))
141 if err != nil {
142 t.Errorf("Parse %v returned err %v", input.in, err)
143 continue
144 }
145 filtered := ast.GetFromPath(nodes, input.path)
146 got := parser.Pretty(filtered, 0)
147 if diff := diff.Diff(input.want, got); diff != "" {
148 t.Errorf("GetFromPath %v %v returned diff (-want, +got):\n%s", input.in, input.path, diff)
149 }
150 }
151 }
152
153 func TestIsCommentOnly(t *testing.T) {
154 inputs := []struct {
155 in string
156 want []bool
157 }{{
158 in: `foo: 1
159 bar: 2`,
160 want: []bool{false, false},
161 },
162 {
163 in: `foo: 1
164 bar: 2
165 `,
166 want: []bool{false, false},
167 },
168 {
169 in: `foo: 1
170 bar: 2
171 # A long trailing comment
172 # over multiple lines.
173 `,
174 want: []bool{false, false, true},
175 },
176 {
177 in: `first {
178 foo: true # bar
179 }
180 `,
181 want: []bool{false},
182 },
183 {
184 in: `first {
185 foo: true # bar
186 }
187 # trailing comment
188 `,
189 want: []bool{false, true},
190 },
191 {
192 in: `{}`,
193 want: []bool{false},
194 },
195 }
196 for _, input := range inputs {
197 nodes, err := parser.Parse([]byte(input.in))
198 if err != nil {
199 t.Errorf("Parse %v returned err %v", input.in, err)
200 continue
201 }
202 if len(nodes) != len(input.want) {
203 t.Errorf("For %v, expect %v nodes, got %v", input.in, len(input.want), len(nodes))
204 }
205 for i, n := range nodes {
206 if got := n.IsCommentOnly(); got != input.want[i] {
207 t.Errorf("For %v, nodes[%v].IsCommentOnly() = %v, want %v", input.in, i, got, input.want[i])
208 }
209 }
210 }
211 }
212
213 func TestFixInline(t *testing.T) {
214 content := `first { }`
215
216 inputs := []struct {
217 in string
218 add string
219 want string
220 }{{
221 in: content,
222 add: "foo: true # bar",
223 want: `first {
224 foo: true # bar
225 }
226 `,
227 }, {
228 in: content,
229 add: `
230 # bar
231 foo: true`,
232 want: `first {
233 # bar
234 foo: true
235 }
236 `,
237 }, {
238 in: content,
239 add: `
240 # bar
241 foo: true # baz`,
242 want: `first {
243 # bar
244 foo: true # baz
245 }
246 `,
247 }, {
248 in: content,
249 add: `
250 foo {
251 bar: true
252 }`,
253 want: `first {
254 foo {
255 bar: true
256 }
257 }
258 `,
259 }, {
260 in: content,
261 add: `foo { bar: { baz: true } zip: "foo" }`,
262 want: `first { foo { bar: { baz: true } zip: "foo" } }
263 `,
264 }, {in: `foo {}`, add: ``, want: `foo {}
265 `}, {in: `foo {
266 }`, add: ``, want: `foo {
267 }
268 `}, {in: `foo <>`, add: ``, want: `foo {}
269 `}, {in: `foo {
270 bar: [
271 1,
272 2
273 ]
274 }`, add: ``, want: `foo {
275 bar: [
276 1,
277 2
278 ]
279 }
280 `}}
281 for _, input := range inputs {
282 nodes, err := parser.Parse([]byte(input.in))
283 if err != nil {
284 t.Errorf("Parse %v returned err %v", input.in, err)
285 continue
286 }
287 if len(nodes) == 0 {
288 t.Errorf("Parse %v returned no nodes", input.in)
289 continue
290 }
291 if input.add != "" {
292 add, err := parser.Parse([]byte(input.add))
293 if err != nil {
294 t.Errorf("Parse %v returned err %v", input.in, err)
295 continue
296 }
297 nodes[0].Children = add
298 }
299 nodes[0].Fix()
300 got := parser.Pretty(nodes, 0)
301 if diff := diff.Diff(input.want, got); diff != "" {
302 t.Errorf("adding %v %v returned diff (-want, +got):\n%s", input.in, input.add, diff)
303 }
304 }
305 }
306
307 func TestListSyntax(t *testing.T) {
308
309 ignoreAstPositionComparer := cmp.Comparer(func(x, y ast.Position) bool {
310 return true
311 })
312
313 inputs := []struct {
314 in string
315 want []*ast.Node
316 }{{
317 in: `foo: []`,
318 want: []*ast.Node{&ast.Node{
319 Name: "foo",
320 ChildrenSameLine: true,
321 ValuesAsList: true}},
322 }, {
323 in: `foo: [
324 {
325 field: val1,
326 other_field: val2
327 },
328 {
329 field: val3,
330 }
331 ]`,
332 want: []*ast.Node{&ast.Node{
333 Name: "foo",
334 ChildrenAsList: true,
335 Children: []*ast.Node{
336 &ast.Node{
337 Name: "",
338 SkipColon: true,
339 Children: []*ast.Node{
340 &ast.Node{Name: "field", Values: []*ast.Value{&ast.Value{Value: "val1"}}},
341 &ast.Node{Name: "other_field", Values: []*ast.Value{&ast.Value{Value: "val2"}}},
342 },
343 },
344 &ast.Node{
345 Name: "",
346 SkipColon: true,
347 Children: []*ast.Node{
348 &ast.Node{Name: "field", Values: []*ast.Value{&ast.Value{Value: "val3"}}},
349 },
350 },
351 }}},
352 }, {
353 in: `foo: {
354 field: val1,
355 other_field: val2
356 }
357 foo: {
358 field: val3,
359 }`,
360 want: []*ast.Node{
361 &ast.Node{
362 Name: "foo",
363 Children: []*ast.Node{
364 &ast.Node{Name: "field", Values: []*ast.Value{&ast.Value{Value: "val1"}}},
365 &ast.Node{Name: "other_field", Values: []*ast.Value{&ast.Value{Value: "val2"}}},
366 },
367 },
368 &ast.Node{
369 Name: "foo",
370 Children: []*ast.Node{
371 &ast.Node{
372 Name: "field",
373 Values: []*ast.Value{&ast.Value{Value: "val3"}},
374 },
375 },
376 }},
377 },
378 }
379 for _, input := range inputs {
380 nodes, err := parser.Parse([]byte(input.in))
381 if err != nil {
382 t.Errorf("Parse returned err %v", err)
383 continue
384 }
385 if diff := cmp.Diff(input.want, nodes, ignoreAstPositionComparer); diff != "" {
386 t.Errorf("Parse() returned unexpected difference in parsed nodes (-want +got):\n%s", diff)
387 }
388 }
389 }
390
View as plain text