blob: 06d92fc5d7ea9d80d109908973ef538720f9c959 [file] [log] [blame]
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +01001// Copyright 2018 The CUE Authors
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 cue
16
17import (
18 "bytes"
19 "fmt"
20 "io/ioutil"
21 "math"
22 "math/big"
23 "reflect"
24 "strings"
25 "testing"
26
Marcel van Lohuizenb9b62d32019-03-14 23:50:15 +010027 "cuelang.org/go/cue/errors"
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010028 "github.com/google/go-cmp/cmp"
29)
30
31func getInstance(t *testing.T, body ...string) *Instance {
32 t.Helper()
33
34 insts := Build(makeInstances([]*bimport{{files: body}}))
35 if insts[0].Err != nil {
36 t.Fatalf("unexpected parse error: %v", insts[0].Err)
37 }
38 return insts[0]
39}
40
41func TestValueType(t *testing.T) {
42 testCases := []struct {
43 value string
44 kind Kind
45 incompleteKind Kind
46 json string
47 valid bool
Marcel van Lohuizen0519f252019-05-06 18:06:48 +020048 concrete bool
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010049 // pos token.Pos
50 }{{ // Not a concrete value.
51 value: `_`,
52 kind: BottomKind,
53 incompleteKind: nextKind - 1,
54 }, {
55 value: `_|_`,
56 kind: BottomKind,
57 incompleteKind: BottomKind,
Marcel van Lohuizen0519f252019-05-06 18:06:48 +020058 concrete: true,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010059 }, {
60 value: `1&2`,
61 kind: BottomKind,
62 incompleteKind: BottomKind,
Marcel van Lohuizen0519f252019-05-06 18:06:48 +020063 concrete: true,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010064 }, { // TODO: should be error{
65 value: `b`,
66 kind: BottomKind,
67 incompleteKind: BottomKind,
Marcel van Lohuizen0519f252019-05-06 18:06:48 +020068 concrete: true,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010069 }, {
70 value: `(b[a])`,
71 kind: BottomKind,
72 incompleteKind: BottomKind,
Marcel van Lohuizen0519f252019-05-06 18:06:48 +020073 concrete: true,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010074 }, { // TODO: should be error{
75 value: `(b)
76 b: bool`,
77 kind: BottomKind,
78 incompleteKind: BoolKind,
79 }, {
80 value: `([][b])`,
81 kind: BottomKind,
82 incompleteKind: BottomKind,
Marcel van Lohuizen0519f252019-05-06 18:06:48 +020083 concrete: true,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010084 }, {
85 value: `null`,
86 kind: NullKind,
87 incompleteKind: NullKind,
Marcel van Lohuizen0519f252019-05-06 18:06:48 +020088 concrete: true,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010089 }, {
90 value: `true`,
91 kind: BoolKind,
92 incompleteKind: BoolKind,
Marcel van Lohuizen0519f252019-05-06 18:06:48 +020093 concrete: true,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010094 }, {
95 value: `false`,
96 kind: BoolKind,
97 incompleteKind: BoolKind,
Marcel van Lohuizen0519f252019-05-06 18:06:48 +020098 concrete: true,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010099 }, {
100 value: `bool`,
101 kind: BottomKind,
102 incompleteKind: BoolKind,
103 }, {
104 value: `2`,
Marcel van Lohuizen4a360992019-05-11 18:18:31 +0200105 kind: IntKind,
106 incompleteKind: IntKind,
Marcel van Lohuizen0519f252019-05-06 18:06:48 +0200107 concrete: true,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100108 }, {
109 value: `2.0`,
Marcel van Lohuizen52cc1092019-05-06 11:43:15 +0200110 kind: FloatKind,
111 incompleteKind: FloatKind,
Marcel van Lohuizen0519f252019-05-06 18:06:48 +0200112 concrete: true,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100113 }, {
114 value: `2.0Mi`,
Marcel van Lohuizen4a360992019-05-11 18:18:31 +0200115 kind: IntKind,
116 incompleteKind: IntKind,
Marcel van Lohuizen0519f252019-05-06 18:06:48 +0200117 concrete: true,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100118 }, {
119 value: `14_000`,
Marcel van Lohuizen4a360992019-05-11 18:18:31 +0200120 kind: IntKind,
121 incompleteKind: IntKind,
Marcel van Lohuizen0519f252019-05-06 18:06:48 +0200122 concrete: true,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100123 }, {
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +0100124 value: `>=0 & <5`,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100125 kind: BottomKind,
126 incompleteKind: NumberKind,
127 }, {
128 value: `float`,
129 kind: BottomKind,
Marcel van Lohuizen52cc1092019-05-06 11:43:15 +0200130 incompleteKind: FloatKind,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100131 }, {
132 value: `"str"`,
133 kind: StringKind,
134 incompleteKind: StringKind,
Marcel van Lohuizen0519f252019-05-06 18:06:48 +0200135 concrete: true,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100136 }, {
137 value: "'''\n'''",
138 kind: BytesKind,
139 incompleteKind: BytesKind,
Marcel van Lohuizen0519f252019-05-06 18:06:48 +0200140 concrete: true,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100141 }, {
142 value: "string",
143 kind: BottomKind,
144 incompleteKind: StringKind,
145 }, {
146 value: `{}`,
147 kind: StructKind,
148 incompleteKind: StructKind,
Marcel van Lohuizen0519f252019-05-06 18:06:48 +0200149 concrete: true,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100150 }, {
151 value: `[]`,
152 kind: ListKind,
153 incompleteKind: ListKind,
Marcel van Lohuizen0519f252019-05-06 18:06:48 +0200154 concrete: true,
155 }, {
156 value: `{a: int, b: [1][a]}.b`,
157 kind: BottomKind,
158 concrete: false,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100159 }}
160 for _, tc := range testCases {
161 t.Run(tc.value, func(t *testing.T) {
162 inst := getInstance(t, tc.value)
163 v := inst.Value()
164 if got := v.Kind(); got != tc.kind {
165 t.Errorf("Kind: got %x; want %x", got, tc.kind)
166 }
167 want := tc.incompleteKind | BottomKind
168 if got := v.IncompleteKind(); got != want {
169 t.Errorf("IncompleteKind: got %x; want %x", got, want)
170 }
171 incomplete := tc.incompleteKind != tc.kind
172 if got := v.IsIncomplete(); got != incomplete {
173 t.Errorf("IsIncomplete: got %v; want %v", got, incomplete)
174 }
Marcel van Lohuizen0519f252019-05-06 18:06:48 +0200175 if got := v.IsConcrete(); got != tc.concrete {
176 t.Errorf("IsConcrete: got %v; want %v", got, tc.concrete)
177 }
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100178 invalid := tc.kind == BottomKind
179 if got := v.IsValid(); got != !invalid {
180 t.Errorf("IsValid: got %v; want %v", got, !invalid)
181 }
182 // if got, want := v.Pos(), tc.pos+1; got != want {
183 // t.Errorf("pos: got %v; want %v", got, want)
184 // }
185 })
186 }
187}
188
189func TestInt(t *testing.T) {
190 testCases := []struct {
191 value string
192 int int64
193 uint uint64
194 base int
195 err string
196 errU string
197 notInt bool
198 }{{
199 value: "1",
200 int: 1,
201 uint: 1,
202 }, {
203 value: "-1",
204 int: -1,
205 uint: 0,
206 errU: ErrAbove.Error(),
207 }, {
208 value: "-111222333444555666777888999000",
209 int: math.MinInt64,
210 uint: 0,
211 err: ErrAbove.Error(),
212 errU: ErrAbove.Error(),
213 }, {
214 value: "111222333444555666777888999000",
215 int: math.MaxInt64,
216 uint: math.MaxUint64,
217 err: ErrBelow.Error(),
218 errU: ErrBelow.Error(),
219 }, {
220 value: "1.0",
221 err: "not of right kind (float vs int)",
222 errU: "not of right kind (float vs int)",
223 notInt: true,
224 }, {
225 value: "int",
226 err: "non-concrete value (int)*",
227 errU: "non-concrete value (int)*",
228 notInt: true,
229 }, {
230 value: "_|_",
231 err: "from source",
232 errU: "from source",
233 notInt: true,
234 }}
235 for _, tc := range testCases {
236 t.Run(tc.value, func(t *testing.T) {
237 n := getInstance(t, tc.value).Value()
238 base := 10
239 if tc.base > 0 {
240 base = tc.base
241 }
242 b, err := n.AppendInt(nil, base)
243 if checkFailed(t, err, tc.err, "append") {
244 want := tc.value
245 if got := string(b); got != want {
246 t.Errorf("append: got %v; want %v", got, want)
247 }
248 }
249
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100250 vi, err := n.Int64()
251 checkErr(t, err, tc.err, "Int64")
252 if vi != tc.int {
253 t.Errorf("Int64: got %v; want %v", vi, tc.int)
254 }
255
256 vu, err := n.Uint64()
257 checkErr(t, err, tc.errU, "Uint64")
258 if vu != uint64(tc.uint) {
259 t.Errorf("Uint64: got %v; want %v", vu, tc.uint)
260 }
261 })
262 }
263}
264
265func TestFloat(t *testing.T) {
266 testCases := []struct {
267 value string
268 float string
269 float64 float64
270 mant string
271 exp int
272 fmt byte
273 prec int
Marcel van Lohuizen52cc1092019-05-06 11:43:15 +0200274 kind Kind
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100275 err string
276 }{{
277 value: "1",
278 float: "1",
279 mant: "1",
280 exp: 0,
281 float64: 1,
282 fmt: 'g',
Marcel van Lohuizen4a360992019-05-11 18:18:31 +0200283 kind: IntKind,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100284 }, {
285 value: "-1",
286 float: "-1",
287 mant: "-1",
288 exp: 0,
289 float64: -1,
290 fmt: 'g',
Marcel van Lohuizen4a360992019-05-11 18:18:31 +0200291 kind: IntKind,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100292 }, {
293 value: "1.0",
294 float: "1.0",
295 mant: "10",
296 exp: -1,
297 float64: 1.0,
298 fmt: 'g',
Marcel van Lohuizen52cc1092019-05-06 11:43:15 +0200299 kind: FloatKind,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100300 }, {
301 value: "2.6",
302 float: "2.6",
303 mant: "26",
304 exp: -1,
305 float64: 2.6,
306 fmt: 'g',
Marcel van Lohuizen52cc1092019-05-06 11:43:15 +0200307 kind: FloatKind,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100308 }, {
309 value: "20.600",
310 float: "20.60",
311 mant: "20600",
312 exp: -3,
313 float64: 20.60,
314 prec: 2,
315 fmt: 'f',
Marcel van Lohuizen52cc1092019-05-06 11:43:15 +0200316 kind: FloatKind,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100317 }, {
318 value: "1/0",
319 float: "∞",
320 float64: math.Inf(1),
321 prec: 2,
322 fmt: 'f',
323 err: ErrAbove.Error(),
Marcel van Lohuizen52cc1092019-05-06 11:43:15 +0200324 kind: FloatKind,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100325 }, {
326 value: "-1/0",
327 float: "-∞",
328 float64: math.Inf(-1),
329 prec: 2,
330 fmt: 'f',
331 err: ErrBelow.Error(),
Marcel van Lohuizen52cc1092019-05-06 11:43:15 +0200332 kind: FloatKind,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100333 }, {
334 value: "1.797693134862315708145274237317043567982e+308",
335 float: "1.8e+308",
336 mant: "1797693134862315708145274237317043567982",
337 exp: 269,
338 float64: math.Inf(1),
339 prec: 2,
340 fmt: 'g',
341 err: ErrAbove.Error(),
Marcel van Lohuizen52cc1092019-05-06 11:43:15 +0200342 kind: FloatKind,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100343 }, {
344 value: "-1.797693134862315708145274237317043567982e+308",
345 float: "-1.8e+308",
346 mant: "-1797693134862315708145274237317043567982",
347 exp: 269,
348 float64: math.Inf(-1),
349 prec: 2,
350 fmt: 'g',
Marcel van Lohuizen52cc1092019-05-06 11:43:15 +0200351 kind: FloatKind,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100352 err: ErrBelow.Error(),
353 }, {
354 value: "4.940656458412465441765687928682213723650e-324",
355 float: "4.941e-324",
356 mant: "4940656458412465441765687928682213723650",
357 exp: -363,
358 float64: 0,
359 prec: 4,
360 fmt: 'g',
Marcel van Lohuizen52cc1092019-05-06 11:43:15 +0200361 kind: FloatKind,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100362 err: ErrBelow.Error(),
363 }, {
364 value: "-4.940656458412465441765687928682213723650e-324",
365 float: "-4.940656458412465441765687928682213723650e-324",
366 mant: "-4940656458412465441765687928682213723650",
367 exp: -363,
368 float64: 0,
369 prec: -1,
370 fmt: 'g',
Marcel van Lohuizen52cc1092019-05-06 11:43:15 +0200371 kind: FloatKind,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100372 err: ErrAbove.Error(),
373 }}
374 for _, tc := range testCases {
375 t.Run(tc.value, func(t *testing.T) {
376 n := getInstance(t, tc.value).Value()
Marcel van Lohuizen52cc1092019-05-06 11:43:15 +0200377 if n.Kind() != tc.kind {
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100378 t.Fatal("Not a number")
379 }
380
381 var mant big.Int
382 exp, err := n.MantExp(&mant)
383 mstr := ""
384 if err == nil {
385 mstr = mant.String()
386 }
387 if exp != tc.exp || mstr != tc.mant {
388 t.Errorf("mantExp: got %s %d; want %s %d", mstr, exp, tc.mant, tc.exp)
389 }
390
391 b, err := n.AppendFloat(nil, tc.fmt, tc.prec)
392 want := tc.float
393 if got := string(b); got != want {
394 t.Errorf("append: got %v; want %v", got, want)
395 }
396
397 f, err := n.Float64()
398 checkErr(t, err, tc.err, "Float64")
399 if f != tc.float64 {
400 t.Errorf("Float64: got %v; want %v", f, tc.float64)
401 }
402 })
403 }
404}
405
406func TestString(t *testing.T) {
407 testCases := []struct {
408 value string
409 str string
410 err string
411 }{{
412 value: `""`,
413 str: ``,
414 }, {
415 value: `"Hello world!"`,
416 str: `Hello world!`,
417 }, {
418 value: `"Hello \(world)!"
419 world: "world"`,
420 str: `Hello world!`,
421 }, {
422 value: `string`,
423 err: "non-concrete value (string)*",
424 }}
425 for _, tc := range testCases {
426 t.Run(tc.value, func(t *testing.T) {
427 str, err := getInstance(t, tc.value).Value().String()
428 checkFatal(t, err, tc.err, "init")
429 if str != tc.str {
430 t.Errorf("String: got %q; want %q", str, tc.str)
431 }
432
433 b, err := getInstance(t, tc.value).Value().Bytes()
434 checkFatal(t, err, tc.err, "init")
435 if got := string(b); got != tc.str {
436 t.Errorf("Bytes: got %q; want %q", got, tc.str)
437 }
438
439 r, err := getInstance(t, tc.value).Value().Reader()
440 checkFatal(t, err, tc.err, "init")
441 b, _ = ioutil.ReadAll(r)
442 if got := string(b); got != tc.str {
443 t.Errorf("Reader: got %q; want %q", got, tc.str)
444 }
445 })
446 }
447}
448
449func TestError(t *testing.T) {
450 testCases := []struct {
451 value string
452 err string
453 }{{
454 value: `_|_`,
455 err: "from source",
456 }, {
457 value: `"Hello world!"`,
458 }, {
459 value: `string`,
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100460 err: "",
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100461 }}
462 for _, tc := range testCases {
463 t.Run(tc.value, func(t *testing.T) {
464 err := getInstance(t, tc.value).Value().Err()
465 checkErr(t, err, tc.err, "init")
466 })
467 }
468}
469
470func TestNull(t *testing.T) {
471 testCases := []struct {
472 value string
473 err string
474 }{{
475 value: `_|_`,
476 err: "from source",
477 }, {
478 value: `"str"`,
479 err: "not of right kind (string vs null)",
480 }, {
481 value: `null`,
482 }, {
483 value: `_`,
484 err: "non-concrete value (_)*",
485 }}
486 for _, tc := range testCases {
487 t.Run(tc.value, func(t *testing.T) {
488 err := getInstance(t, tc.value).Value().Null()
489 checkErr(t, err, tc.err, "init")
490 })
491 }
492}
493
494func TestBool(t *testing.T) {
495 testCases := []struct {
496 value string
497 bool bool
498 err string
499 }{{
500 value: `_|_`,
501 err: "from source",
502 }, {
503 value: `"str"`,
504 err: "not of right kind (string vs bool)",
505 }, {
506 value: `true`,
507 bool: true,
508 }, {
509 value: `false`,
510 }, {
511 value: `bool`,
512 err: "non-concrete value (bool)*",
513 }}
514 for _, tc := range testCases {
515 t.Run(tc.value, func(t *testing.T) {
516 got, err := getInstance(t, tc.value).Value().Bool()
517 if checkErr(t, err, tc.err, "init") {
518 if got != tc.bool {
519 t.Errorf("got %v; want %v", got, tc.bool)
520 }
521 }
522 })
523 }
524}
525
526func TestList(t *testing.T) {
527 testCases := []struct {
528 value string
529 res string
530 err string
531 }{{
532 value: `_|_`,
533 err: "from source",
534 }, {
535 value: `"str"`,
536 err: "not of right kind (string vs list)",
537 }, {
538 value: `[]`,
539 res: "[]",
540 }, {
541 value: `[1,2,3]`,
542 res: "[1,2,3,]",
543 }, {
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +0100544 value: `>=5*[1,2,3, ...int]`,
Jonathan Amsterdam0500c312019-02-16 18:04:09 -0500545 err: "incomplete",
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100546 }, {
547 value: `[x for x in y if x > 1]
548 y: [1,2,3]`,
549 res: "[2,3,]",
550 }, {
551 value: `[int]`,
552 err: "cannot convert incomplete value",
553 }}
554 for _, tc := range testCases {
555 t.Run(tc.value, func(t *testing.T) {
556 l, err := getInstance(t, tc.value).Value().List()
557 checkFatal(t, err, tc.err, "init")
558
559 buf := []byte{'['}
560 for l.Next() {
561 b, err := l.Value().MarshalJSON()
562 checkFatal(t, err, tc.err, "list.Value")
563 buf = append(buf, b...)
564 buf = append(buf, ',')
565 }
566 buf = append(buf, ']')
567 if got := string(buf); got != tc.res {
568 t.Errorf("got %v; want %v", got, tc.res)
569 }
570 })
571 }
572}
573
574func TestFields(t *testing.T) {
575 testCases := []struct {
576 value string
577 res string
578 err string
579 }{{
580 value: `_|_`,
581 err: "from source",
582 }, {
583 value: `"str"`,
584 err: "not of right kind (string vs struct)",
585 }, {
586 value: `{}`,
587 res: "{}",
588 }, {
589 value: `{a:1,b:2,c:3}`,
590 res: "{a:1,b:2,c:3,}",
591 }, {
592 value: `{a:1,"_b":2,c:3,_d:4}`,
593 res: "{a:1,_b:2,c:3,}",
594 }, {
595 value: `{_a:"a"}`,
596 res: "{}",
597 }, {
Marcel van Lohuizen76b92b52018-12-16 10:47:03 +0100598 value: `{"\(k)": v for k, v in y if v > 1}
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100599 y: {a:1,b:2,c:3}`,
600 res: "{b:2,c:3,}",
601 }, {
602 value: `{a:1,b:2,c:int}`,
603 err: "cannot convert incomplete value",
604 }}
605 for _, tc := range testCases {
606 t.Run(tc.value, func(t *testing.T) {
607 obj := getInstance(t, tc.value).Value()
608
609 iter, err := obj.Fields()
610 checkFatal(t, err, tc.err, "init")
611
612 buf := []byte{'{'}
613 for iter.Next() {
614 buf = append(buf, iter.Label()...)
615 buf = append(buf, ':')
616 b, err := iter.Value().MarshalJSON()
617 checkFatal(t, err, tc.err, "Obj.At")
618 buf = append(buf, b...)
619 buf = append(buf, ',')
620 }
621 buf = append(buf, '}')
622 if got := string(buf); got != tc.res {
623 t.Errorf("got %v; want %v", got, tc.res)
624 }
625
626 iter, _ = obj.Fields()
627 for iter.Next() {
628 want, err := iter.Value().MarshalJSON()
629 checkFatal(t, err, tc.err, "Obj.At2")
630
631 got, err := obj.Lookup(iter.Label()).MarshalJSON()
632 checkFatal(t, err, tc.err, "Obj.At2")
633
634 if !bytes.Equal(got, want) {
635 t.Errorf("Lookup: got %q; want %q", got, want)
636 }
637 }
638 v := obj.Lookup("non-existing")
639 checkErr(t, v.Err(), "not found", "non-existing")
640 })
641 }
642}
643
644func TestAllFields(t *testing.T) {
645 testCases := []struct {
646 value string
647 res string
648 err string
649 }{{
650 value: `{a:1,"_b":2,c:3,_d:4}`,
651 res: "{a:1,_b:2,c:3,_d:4,}",
652 }, {
653 value: `{_a:"a"}`,
654 res: `{_a:"a",}`,
655 }}
656 for _, tc := range testCases {
657 t.Run(tc.value, func(t *testing.T) {
658 obj := getInstance(t, tc.value).Value()
659
Marcel van Lohuizen466e3f62019-04-06 14:16:50 +0200660 iter, err := obj.Fields(All())
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100661 checkFatal(t, err, tc.err, "init")
662
663 buf := []byte{'{'}
664 for iter.Next() {
665 buf = append(buf, iter.Label()...)
666 buf = append(buf, ':')
667 b, err := iter.Value().MarshalJSON()
668 checkFatal(t, err, tc.err, "Obj.At")
669 buf = append(buf, b...)
670 buf = append(buf, ',')
671 }
672 buf = append(buf, '}')
673 if got := string(buf); got != tc.res {
674 t.Errorf("got %v; want %v", got, tc.res)
675 }
676 })
677 }
678}
679
Marcel van Lohuizen0519f252019-05-06 18:06:48 +0200680func TestDefaults(t *testing.T) {
681 testCases := []struct {
682 value string
683 def string
684 val string
685 err string
686 }{{
687 value: `number | *1`,
688 def: "1",
689 val: "number",
690 }, {
691 value: `1 | 2 | *3`,
692 def: "3",
693 val: "1|2|3",
694 }, {
695 value: `*{a:1,b:2}|{a:1}|{b:2}`,
696 def: "<0>{a: 1, b: 2}",
697 val: "<0>{a: 1}|<0>{b: 2}",
698 }}
699 for _, tc := range testCases {
700 t.Run(tc.value, func(t *testing.T) {
701 v := getInstance(t, "a: "+tc.value).Lookup("a")
702
703 _, val := v.Expr()
704 d, _ := v.Default()
705
706 if got := fmt.Sprint(d); got != tc.def {
707 t.Errorf("default: got %v; want %v", got, tc.def)
708 }
709
710 vars := []string{}
711 for _, v := range val {
712 vars = append(vars, fmt.Sprint(v))
713 }
714 if got := strings.Join(vars, "|"); got != tc.val {
715 t.Errorf("value: got %v; want %v", got, tc.val)
716 }
717 })
718 }
719}
720
721func TestLen(t *testing.T) {
722 testCases := []struct {
723 input string
724 length string
725 }{{
726 input: "[1, 3]",
727 length: "2",
728 }, {
729 input: "[1, 3, ...]",
Marcel van Lohuizen4a360992019-05-11 18:18:31 +0200730 length: "int & >=2",
Marcel van Lohuizen0519f252019-05-06 18:06:48 +0200731 }, {
732 input: `"foo"`,
733 length: "3",
734 }, {
735 input: `'foo'`,
736 length: "3",
737 // TODO: Currently not supported.
738 // }, {
739 // input: "{a:1, b:3, a:1, c?: 3, _hidden: 4}",
740 // length: "2",
741 }, {
742 input: "3",
Marcel van Lohuizen4a360992019-05-11 18:18:31 +0200743 length: "_|_(3:len not supported for type 8)",
Marcel van Lohuizen0519f252019-05-06 18:06:48 +0200744 }}
745 for _, tc := range testCases {
746 t.Run(tc.input, func(t *testing.T) {
747 v := getInstance(t, "a: "+tc.input).Lookup("a")
748
749 length := v.Len()
750 if got := fmt.Sprint(length); got != tc.length {
751 t.Errorf("length: got %v; want %v", got, tc.length)
752 }
753 })
754 }
755}
756
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100757func TestTemplate(t *testing.T) {
758 testCases := []struct {
759 value string
760 path []string
761 want string
762 }{{
763 value: `
764 a <Name>: Name
765 `,
766 path: []string{"a", ""},
767 want: `"label"`,
768 }, {
769 value: `
770 <Name>: { a: Name }
771 `,
772 path: []string{"", "a"},
773 want: `"label"`,
774 }, {
775 value: `
776 <Name>: { a: Name }
777 `,
778 path: []string{""},
779 want: `{"a":"label"}`,
780 }, {
781 value: `
782 a <Foo> <Bar>: { b: Foo+Bar }
783 `,
784 path: []string{"a", "", ""},
785 want: `{"b":"labellabel"}`,
786 }, {
787 value: `
788 a <Foo> b <Bar>: { c: Foo+Bar }
789 a foo b <Bar>: { d: Bar }
790 `,
791 path: []string{"a", "foo", "b", ""},
792 want: `{"c":"foolabel","d":"label"}`,
793 }}
794 for _, tc := range testCases {
795 t.Run("", func(t *testing.T) {
796 v := getInstance(t, tc.value).Value()
797 for _, p := range tc.path {
798 if p == "" {
799 v = v.Template()("label")
800 } else {
801 v = v.Lookup(p)
802 }
803 }
804 b, err := v.MarshalJSON()
805 if err != nil {
806 t.Fatal(err)
807 }
808 if got := string(b); got != tc.want {
809 t.Errorf("\n got: %q\nwant: %q", got, tc.want)
810 }
811 })
812 }
813}
814
815func TestSubsumes(t *testing.T) {
816 a := []string{"a"}
817 b := []string{"b"}
818 testCases := []struct {
819 value string
820 pathA []string
821 pathB []string
822 want bool
823 }{{
824 value: `4`,
825 want: true,
826 }, {
827 value: `a: string, b: "foo"`,
828 pathA: a,
829 pathB: b,
830 want: true,
831 }, {
832 value: `a: string, b: "foo"`,
833 pathA: b,
834 pathB: a,
835 want: false,
836 }, {
837 value: `a: {a: string, b: 4}, b: {a: "foo", b: 4}`,
838 pathA: a,
839 pathB: b,
840 want: true,
841 }, {
842 value: `a: [string, 4], b: ["foo", 4]`,
843 pathA: a,
844 pathB: b,
845 want: true,
846 }}
847 for _, tc := range testCases {
848 t.Run(tc.value, func(t *testing.T) {
849 v := getInstance(t, tc.value)
850 a := v.Lookup(tc.pathA...)
851 b := v.Lookup(tc.pathB...)
852 got := a.Subsumes(b)
853 if got != tc.want {
854 t.Errorf("got %v (%v); want %v (%v)", got, a, tc.want, b)
855 }
856 })
857 }
858}
859
860func TestUnify(t *testing.T) {
861 a := []string{"a"}
862 b := []string{"b"}
863 testCases := []struct {
864 value string
865 pathA []string
866 pathB []string
867 want string
868 }{{
869 value: `4`,
870 want: `4`,
871 }, {
872 value: `a: string, b: "foo"`,
873 pathA: a,
874 pathB: b,
875 want: `"foo"`,
876 }, {
877 value: `a: string, b: "foo"`,
878 pathA: b,
879 pathB: a,
880 want: `"foo"`,
881 }, {
882 value: `a: {a: string, b: 4}, b: {a: "foo", b: 4}`,
883 pathA: a,
884 pathB: b,
885 want: `{"a":"foo","b":4}`,
886 }, {
887 value: `a: [string, 4], b: ["foo", 4]`,
888 pathA: a,
889 pathB: b,
890 want: `["foo",4]`,
891 }}
892 for _, tc := range testCases {
893 t.Run(tc.value, func(t *testing.T) {
894 v := getInstance(t, tc.value).Value()
895 x := v.Lookup(tc.pathA...)
896 y := v.Lookup(tc.pathB...)
897 b, err := x.Unify(y).MarshalJSON()
898 if err != nil {
899 t.Fatal(err)
900 }
901 if got := string(b); got != tc.want {
902 t.Errorf("got %v; want %v", got, tc.want)
903 }
904 })
905 }
906}
907
908func TestDecode(t *testing.T) {
909 type fields struct {
910 A int `json:"A"`
911 B int `json:"B"`
912 C int `json:"C"`
913 }
914 intList := func(ints ...int) *[]int {
915 ints = append([]int{}, ints...)
916 return &ints
917 }
918 testCases := []struct {
919 value string
920 dst interface{}
921 want interface{}
922 err string
923 }{{
924 value: `_|_`,
925 err: "from source",
926 }, {
927 value: `"str"`,
928 dst: "",
929 want: "str",
930 }, {
931 value: `"str"`,
932 dst: new(int),
933 err: "cannot unmarshal string into Go value of type int",
934 }, {
935 value: `{}`,
936 dst: &fields{},
937 want: &fields{},
938 }, {
939 value: `{a:1,b:2,c:3}`,
940 dst: &fields{},
941 want: &fields{A: 1, B: 2, C: 3},
942 }, {
Marcel van Lohuizen76b92b52018-12-16 10:47:03 +0100943 value: `{"\(k)": v for k, v in y if v > 1}
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100944 y: {a:1,b:2,c:3}`,
945 dst: &fields{},
946 want: &fields{B: 2, C: 3},
947 }, {
948 value: `{a:1,b:2,c:int}`,
949 dst: new(fields),
950 err: "cannot convert incomplete value",
951 }, {
952 value: `[]`,
953 dst: intList(),
954 want: intList(),
955 }, {
956 value: `[1,2,3]`,
957 dst: intList(),
958 want: intList(1, 2, 3),
959 }, {
960 value: `[x for x in y if x > 1]
961 y: [1,2,3]`,
962 dst: intList(),
963 want: intList(2, 3),
964 }, {
965 value: `[int]`,
966 err: "cannot convert incomplete value",
967 }}
968 for _, tc := range testCases {
969 t.Run(tc.value, func(t *testing.T) {
970 err := getInstance(t, tc.value).Value().Decode(&tc.dst)
971 checkFatal(t, err, tc.err, "init")
972
973 if !cmp.Equal(tc.dst, tc.want) {
974 t.Error(cmp.Diff(tc.dst, tc.want))
975 t.Errorf("\n%#v\n%#v", tc.dst, tc.want)
976 }
977 })
978 }
979}
980
981func TestValueLookup(t *testing.T) {
982 config := `
983 a: {
984 a: 0
985 b: 1
986 c: 2
987 }
988 b: {
989 d: a.a
990 e: int
991 }
992 `
993
994 strList := func(s ...string) []string { return s }
995
996 testCases := []struct {
997 config string
998 path []string
999 str string
1000 notExists bool
1001 }{{
1002 config: "_|_",
1003 path: strList(""),
1004 str: "from source",
1005 }, {
1006 config: "_|_",
1007 path: strList("a"),
1008 str: "from source",
1009 }, {
1010 config: config,
1011 path: strList(),
1012 str: "<0>{a: <1>{a: 0, b: 1, c: 2}, b: <2>{d: <0>.a.a, e: int}",
1013 }, {
1014 config: config,
1015 path: strList("a", "a"),
1016 str: "0",
1017 }, {
1018 config: config,
1019 path: strList("a"),
1020 str: "<0>{a: 0, b: 1, c: 2}",
1021 }, {
1022 config: config,
1023 path: strList("b", "d"),
1024 str: "0",
1025 }, {
1026 config: config,
1027 path: strList("c", "non-existing"),
1028 str: "not found",
1029 notExists: true,
1030 }, {
1031 config: config,
1032 path: strList("b", "d", "lookup in non-struct"),
Marcel van Lohuizen4a360992019-05-11 18:18:31 +02001033 str: "not of right kind (int vs struct)",
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +01001034 }}
1035 for _, tc := range testCases {
1036 t.Run(tc.str, func(t *testing.T) {
1037 v := getInstance(t, tc.config).Value().Lookup(tc.path...)
1038 if got := !v.Exists(); got != tc.notExists {
1039 t.Errorf("exists: got %v; want %v", got, tc.notExists)
1040 }
1041
1042 got := fmt.Sprint(v)
1043 if tc.str == "" {
1044 t.Fatalf("str empty, got %q", got)
1045 }
1046 if !strings.Contains(got, tc.str) {
1047 t.Errorf("\n got %v\nwant %v", got, tc.str)
1048 }
1049 })
1050 }
1051}
1052
Marcel van Lohuizenb9b62d32019-03-14 23:50:15 +01001053func cmpError(a, b error) bool {
1054 if a == nil {
1055 return b == nil
1056 }
1057 if b == nil {
1058 return a == nil
1059 }
1060 return a.Error() == b.Error()
1061}
1062
1063func TestAttributeErr(t *testing.T) {
1064 const config = `
1065 a: {
1066 a: 0 @foo(a,b,c=1)
1067 b: 1 @bar(a,b,c,d=1) @foo(a,,d=1)
1068 }
1069 `
1070 testCases := []struct {
1071 path string
1072 attr string
1073 err error
1074 }{{
1075 path: "a",
1076 attr: "foo",
1077 err: nil,
1078 }, {
1079 path: "a",
1080 attr: "bar",
1081 err: errors.New("undefined value"),
1082 }, {
1083 path: "xx",
1084 attr: "bar",
1085 err: errors.New("undefined value"),
1086 }, {
1087 path: "e",
1088 attr: "bar",
1089 err: errors.New("undefined value"),
1090 }}
1091 for _, tc := range testCases {
1092 t.Run(tc.path+"-"+tc.attr, func(t *testing.T) {
1093 v := getInstance(t, config).Value().Lookup("a", tc.path)
1094 a := v.Attribute(tc.attr)
1095 err := a.Err()
1096 if !cmpError(err, tc.err) {
1097 t.Errorf("got %v; want %v", err, tc.err)
1098 }
1099 })
1100 }
1101}
1102
1103func TestAttributeString(t *testing.T) {
1104 const config = `
1105 a: {
1106 a: 0 @foo(a,b,c=1)
1107 b: 1 @bar(a,b,c,d=1) @foo(a,,d=1)
1108 }
1109 `
1110 testCases := []struct {
1111 path string
1112 attr string
1113 pos int
1114 str string
1115 err error
1116 }{{
1117 path: "a",
1118 attr: "foo",
1119 pos: 0,
1120 str: "a",
1121 }, {
1122 path: "a",
1123 attr: "foo",
1124 pos: 2,
1125 str: "c=1",
1126 }, {
1127 path: "b",
1128 attr: "bar",
1129 pos: 3,
1130 str: "d=1",
1131 }, {
1132 path: "e",
1133 attr: "bar",
1134 err: errors.New("undefined value"),
1135 }, {
1136 path: "b",
1137 attr: "foo",
1138 pos: 4,
1139 err: errors.New("field does not exist"),
1140 }}
1141 for _, tc := range testCases {
1142 t.Run(fmt.Sprintf("%s.%s:%d", tc.path, tc.attr, tc.pos), func(t *testing.T) {
1143 v := getInstance(t, config).Value().Lookup("a", tc.path)
1144 a := v.Attribute(tc.attr)
1145 got, err := a.String(tc.pos)
1146 if !cmpError(err, tc.err) {
1147 t.Errorf("err: got %v; want %v", err, tc.err)
1148 }
1149 if got != tc.str {
1150 t.Errorf("str: got %v; want %v", got, tc.str)
1151 }
1152 })
1153 }
1154}
1155
1156func TestAttributeInt(t *testing.T) {
1157 const config = `
1158 a: {
1159 a: 0 @foo(1,3,c=1)
1160 b: 1 @bar(a,-4,c,d=1) @foo(a,,d=1)
1161 }
1162 `
1163 testCases := []struct {
1164 path string
1165 attr string
1166 pos int
1167 val int64
1168 err error
1169 }{{
1170 path: "a",
1171 attr: "foo",
1172 pos: 0,
1173 val: 1,
1174 }, {
1175 path: "b",
1176 attr: "bar",
1177 pos: 1,
1178 val: -4,
1179 }, {
1180 path: "e",
1181 attr: "bar",
1182 err: errors.New("undefined value"),
1183 }, {
1184 path: "b",
1185 attr: "foo",
1186 pos: 4,
1187 err: errors.New("field does not exist"),
1188 }, {
1189 path: "a",
1190 attr: "foo",
1191 pos: 2,
1192 err: errors.New(`strconv.ParseInt: parsing "c=1": invalid syntax`),
1193 }}
1194 for _, tc := range testCases {
1195 t.Run(fmt.Sprintf("%s.%s:%d", tc.path, tc.attr, tc.pos), func(t *testing.T) {
1196 v := getInstance(t, config).Value().Lookup("a", tc.path)
1197 a := v.Attribute(tc.attr)
1198 got, err := a.Int(tc.pos)
1199 if !cmpError(err, tc.err) {
1200 t.Errorf("err: got %v; want %v", err, tc.err)
1201 }
1202 if got != tc.val {
1203 t.Errorf("val: got %v; want %v", got, tc.val)
1204 }
1205 })
1206 }
1207}
1208
1209func TestAttributeFlag(t *testing.T) {
1210 const config = `
1211 a: {
1212 a: 0 @foo(a,b,c=1)
1213 b: 1 @bar(a,b,c,d=1) @foo(a,,d=1)
1214 }
1215 `
1216 testCases := []struct {
1217 path string
1218 attr string
1219 pos int
1220 flag string
1221 val bool
1222 err error
1223 }{{
1224 path: "a",
1225 attr: "foo",
1226 pos: 0,
1227 flag: "a",
1228 val: true,
1229 }, {
1230 path: "b",
1231 attr: "bar",
1232 pos: 1,
1233 flag: "a",
1234 val: false,
1235 }, {
1236 path: "b",
1237 attr: "bar",
1238 pos: 0,
1239 flag: "c",
1240 val: true,
1241 }, {
1242 path: "e",
1243 attr: "bar",
1244 err: errors.New("undefined value"),
1245 }, {
1246 path: "b",
1247 attr: "foo",
1248 pos: 4,
1249 err: errors.New("field does not exist"),
1250 }}
1251 for _, tc := range testCases {
1252 t.Run(fmt.Sprintf("%s.%s:%d", tc.path, tc.attr, tc.pos), func(t *testing.T) {
1253 v := getInstance(t, config).Value().Lookup("a", tc.path)
1254 a := v.Attribute(tc.attr)
1255 got, err := a.Flag(tc.pos, tc.flag)
1256 if !cmpError(err, tc.err) {
1257 t.Errorf("err: got %v; want %v", err, tc.err)
1258 }
1259 if got != tc.val {
1260 t.Errorf("val: got %v; want %v", got, tc.val)
1261 }
1262 })
1263 }
1264}
1265
1266func TestAttributeLookup(t *testing.T) {
1267 const config = `
1268 a: {
1269 a: 0 @foo(a,b,c=1)
1270 b: 1 @bar(a,b,e=-5,d=1) @foo(a,,d=1)
1271 }
1272 `
1273 testCases := []struct {
1274 path string
1275 attr string
1276 pos int
1277 key string
1278 val string
1279 err error
1280 }{{
1281 path: "a",
1282 attr: "foo",
1283 pos: 0,
1284 key: "c",
1285 val: "1",
1286 }, {
1287 path: "b",
1288 attr: "bar",
1289 pos: 1,
1290 key: "a",
1291 val: "",
1292 }, {
1293 path: "b",
1294 attr: "bar",
1295 pos: 0,
1296 key: "e",
1297 val: "-5",
1298 }, {
1299 path: "b",
1300 attr: "bar",
1301 pos: 0,
1302 key: "d",
1303 val: "1",
1304 }, {
1305 path: "b",
1306 attr: "foo",
1307 pos: 2,
1308 key: "d",
1309 val: "1",
1310 }, {
1311 path: "b",
1312 attr: "foo",
1313 pos: 2,
1314 key: "f",
1315 val: "",
1316 }, {
1317 path: "e",
1318 attr: "bar",
1319 err: errors.New("undefined value"),
1320 }, {
1321 path: "b",
1322 attr: "foo",
1323 pos: 4,
1324 err: errors.New("field does not exist"),
1325 }}
1326 for _, tc := range testCases {
1327 t.Run(fmt.Sprintf("%s.%s:%d", tc.path, tc.attr, tc.pos), func(t *testing.T) {
1328 v := getInstance(t, config).Value().Lookup("a", tc.path)
1329 a := v.Attribute(tc.attr)
1330 got, _, err := a.Lookup(tc.pos, tc.key)
1331 if !cmpError(err, tc.err) {
1332 t.Errorf("err: got %v; want %v", err, tc.err)
1333 }
1334 if got != tc.val {
1335 t.Errorf("val: got %v; want %v", got, tc.val)
1336 }
1337 })
1338 }
1339}
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +01001340func TestMashalJSON(t *testing.T) {
1341 testCases := []struct {
1342 value string
1343 json string
1344 err string
1345 }{{
1346 value: `""`,
1347 json: `""`,
1348 }, {
1349 value: `null`,
1350 json: `null`,
1351 }, {
1352 value: `_|_`,
1353 err: "from source",
1354 }, {
1355 value: `(a.b)
1356 a: {}`,
1357 err: "undefined field",
1358 }, {
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +01001359 value: `true`,
1360 json: `true`,
1361 }, {
1362 value: `false`,
1363 json: `false`,
1364 }, {
1365 value: `bool`,
1366 json: `bool`,
1367 err: "cannot convert incomplete value",
1368 }, {
1369 value: `"str"`,
1370 json: `"str"`,
1371 }, {
1372 value: `12_000`,
1373 json: `12000`,
1374 }, {
1375 value: `12.000`,
1376 json: `12.000`,
1377 }, {
1378 value: `12M`,
1379 json: `12000000`,
1380 }, {
1381 value: `3.0e100`,
1382 json: `3.0E+100`,
1383 }, {
1384 value: `[]`,
1385 json: `[]`,
1386 }, {
1387 value: `[1, 2, 3]`,
1388 json: `[1,2,3]`,
1389 }, {
1390 value: `[int]`,
1391 err: `cannot convert incomplete value`,
1392 }, {
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +01001393 value: `(>=3 * [1, 2])`,
Jonathan Amsterdam0500c312019-02-16 18:04:09 -05001394 err: "incomplete error", // TODO: improve error
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +01001395 }, {
1396 value: `{}`,
1397 json: `{}`,
1398 }, {
1399 value: `{a: 2, b: 3, c: ["A", "B"]}`,
1400 json: `{"a":2,"b":3,"c":["A","B"]}`,
Marcel van Lohuizen08a0ef22019-03-28 09:12:19 +01001401 }, {
1402 value: `{foo?: 1, bar?: 2, baz: 3}`,
1403 json: `{"baz":3}`,
1404 }, {
1405 // Has an unresolved cycle, but should not matter as all fields involved
1406 // are optional
1407 value: `{foo?: bar, bar?: foo, baz: 3}`,
1408 json: `{"baz":3}`,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +01001409 }}
1410 for i, tc := range testCases {
1411 t.Run(fmt.Sprintf("%d/%v", i, tc.value), func(t *testing.T) {
1412 inst := getInstance(t, tc.value)
1413 b, err := inst.Value().MarshalJSON()
1414 checkFatal(t, err, tc.err, "init")
1415
1416 if got := string(b); got != tc.json {
1417 t.Errorf("\n got %v;\nwant %v", got, tc.json)
1418 }
1419 })
1420 }
1421}
1422
1423func TestWalk(t *testing.T) {
1424 testCases := []struct {
1425 value string
1426 out string
1427 }{{
1428 value: `""`,
1429 out: `""`,
1430 }, {
1431 value: `null`,
1432 out: `null`,
1433 }, {
1434 value: `_|_`,
1435 out: "_|_(from source)",
1436 }, {
1437 value: `(a.b)
1438 a: {}`,
1439 out: `_|_(<0>.a.b:undefined field "b")`,
1440 }, {
1441 value: `true`,
1442 out: `true`,
1443 }, {
1444 value: `false`,
1445 out: `false`,
1446 }, {
1447 value: `bool`,
1448 out: "bool",
1449 }, {
1450 value: `"str"`,
1451 out: `"str"`,
1452 }, {
1453 value: `12_000`,
1454 out: `12000`,
1455 }, {
1456 value: `12.000`,
1457 out: `12.000`,
1458 }, {
1459 value: `12M`,
1460 out: `12000000`,
1461 }, {
1462 value: `3.0e100`,
1463 out: `3.0e+100`,
1464 }, {
1465 value: `[]`,
1466 out: `[]`,
1467 }, {
1468 value: `[1, 2, 3]`,
1469 out: `[1,2,3]`,
1470 }, {
1471 value: `[int]`,
1472 out: `[int]`,
1473 }, {
Jonathan Amsterdam0500c312019-02-16 18:04:09 -05001474 value: `3 * [1, 2]`,
1475 out: `[1,2,1,2,1,2]`,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +01001476 }, {
1477 value: `{}`,
1478 out: `{}`,
1479 }, {
1480 value: `{a: 2, b: 3, c: ["A", "B"]}`,
1481 out: `{a:2,b:3,c:["A","B"]}`,
1482 }}
1483 for i, tc := range testCases {
1484 t.Run(fmt.Sprintf("%d/%v", i, tc.value), func(t *testing.T) {
1485 inst := getInstance(t, tc.value)
1486 buf := []byte{}
1487 stripComma := func() {
1488 if n := len(buf) - 1; buf[n] == ',' {
1489 buf = buf[:n]
1490 }
1491 }
1492 inst.Value().Walk(func(v Value) bool {
1493 if k, ok := v.Label(); ok {
1494 buf = append(buf, k+":"...)
1495 }
1496 switch v.Kind() {
1497 case StructKind:
1498 buf = append(buf, '{')
1499 case ListKind:
1500 buf = append(buf, '[')
1501 default:
1502 buf = append(buf, fmt.Sprint(v, ",")...)
1503 }
1504 return true
1505 }, func(v Value) {
1506 switch v.Kind() {
1507 case StructKind:
1508 stripComma()
1509 buf = append(buf, "},"...)
1510 case ListKind:
1511 stripComma()
1512 buf = append(buf, "],"...)
1513 }
1514 })
1515 stripComma()
1516 if got := string(buf); got != tc.out {
1517 t.Errorf("\n got %v;\nwant %v", got, tc.out)
1518 }
1519 })
1520 }
1521}
1522
1523func TestTrimZeros(t *testing.T) {
1524 testCases := []struct {
1525 in string
1526 out string
1527 }{
1528 {"", ""},
1529 {"2", "2"},
1530 {"2.0", "2.0"},
1531 {"2.000000000000", "2.0"},
1532 {"2000000000000", "2e+12"},
1533 {"2000000", "2e+6"},
1534 }
1535 for _, tc := range testCases {
1536 t.Run(tc.in, func(t *testing.T) {
1537 if got := trimZeros(tc.in); got != tc.out {
1538 t.Errorf("got %q; want %q", got, tc.out)
1539 }
1540 })
1541 }
1542}
1543
Marcel van Lohuizen0519f252019-05-06 18:06:48 +02001544func TestReference(t *testing.T) {
1545 testCases := []struct {
1546 input string
1547 want string
1548 }{{
1549 input: "v: _|_",
1550 want: "",
1551 }, {
1552 input: "v: 2",
1553 want: "",
1554 }, {
1555 input: "v: a, a: 1",
1556 want: "a",
1557 }, {
1558 input: "v: a.b.c, a b c: 1",
1559 want: "a b c",
1560 }}
1561 for _, tc := range testCases {
1562 t.Run("", func(t *testing.T) {
1563 v := getInstance(t, tc.input).Lookup("v")
1564 if got := strings.Join(v.Reference(), " "); got != tc.want {
1565 t.Errorf("\n got %v;\nwant %v", got, tc.want)
1566 }
1567 })
1568 }
1569}
1570
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +01001571func TestReferences(t *testing.T) {
1572 config1 := `
1573 a: {
1574 b: 3
1575 }
1576 c: {
1577 d: a.b
1578 e: c.d
1579 f: a
1580 }
1581 `
1582 config2 := `
1583 a: { c: 3 }
1584 b: { c: int, d: 4 }
1585 r: (a & b).c
1586 `
1587 testCases := []struct {
1588 config string
1589 in string
1590 out string
1591 }{
1592 {config1, "c.d", "a.b"},
1593 {config1, "c.e", "c.d"},
1594 {config1, "c.f", "a"},
1595
1596 {config2, "r", "a.c b.c"},
1597 }
1598 for _, tc := range testCases {
1599 t.Run(tc.in, func(t *testing.T) {
1600 ctx, st := compileFile(t, tc.config)
1601 v := newValueRoot(ctx, st)
1602 for _, k := range strings.Split(tc.in, ".") {
1603 obj, err := v.structVal(ctx)
1604 if err != nil {
1605 t.Fatal(err)
1606 }
1607 v = obj.Lookup(k)
1608 }
1609 got := []string{}
1610 for _, r := range v.References() {
1611 got = append(got, strings.Join(r, "."))
1612 }
1613 want := strings.Split(tc.out, " ")
1614 if !reflect.DeepEqual(got, want) {
1615 t.Errorf("got %v; want %v", got, want)
1616 }
1617 })
1618 }
1619}
1620
1621func checkErr(t *testing.T, err error, str, name string) bool {
1622 t.Helper()
1623 if err == nil {
1624 if str != "" {
1625 t.Errorf(`err:%s: got ""; want %q`, name, str)
1626 }
1627 return true
1628 }
1629 return checkFailed(t, err, str, name)
1630}
1631
1632func checkFatal(t *testing.T, err error, str, name string) {
1633 t.Helper()
1634 if !checkFailed(t, err, str, name) {
1635 t.SkipNow()
1636 }
1637}
1638
1639func checkFailed(t *testing.T, err error, str, name string) bool {
1640 t.Helper()
1641 if err != nil {
1642 got := err.Error()
1643 if str == "" {
1644 t.Fatalf(`err:%s: got %q; want ""`, name, got)
1645 }
1646 if !strings.Contains(got, str) {
1647 t.Errorf(`err:%s: got %q; want %q`, name, got, str)
1648 }
1649 return false
1650 }
1651 return true
1652}
Marcel van Lohuizen0519f252019-05-06 18:06:48 +02001653
1654func TestExpr(t *testing.T) {
1655 testCases := []struct {
1656 input string
1657 want string
1658 }{{
1659 input: "v: 3",
1660 want: " 3",
1661 }, {
1662 input: "v: 3 + 4",
1663 want: "+ 3 4",
1664 }, {
1665 input: "v: !a, a: 3",
1666 want: "! <0>.a",
1667 }, {
1668 input: "v: 1 | 2 | 3 | *4",
1669 want: "| 1 2 3 4",
1670 }, {
1671 input: "v: 2 & 5",
1672 want: "& 2 5",
1673 }, {
1674 input: "v: 2 | 5",
1675 want: "| 2 5",
1676 }, {
1677 input: "v: 2 && 5",
1678 want: "&& 2 5",
1679 }, {
1680 input: "v: 2 || 5",
1681 want: "|| 2 5",
1682 }, {
1683 input: "v: 2 == 5",
1684 want: "== 2 5",
1685 }, {
1686 input: "v: !b, b: true",
1687 want: "! <0>.b",
1688 }, {
1689 input: "v: 2 != 5",
1690 want: "!= 2 5",
1691 }, {
1692 input: "v: <5",
1693 want: "< 5",
1694 }, {
1695 input: "v: 2 <= 5",
1696 want: "<= 2 5",
1697 }, {
1698 input: "v: 2 > 5",
1699 want: "> 2 5",
1700 }, {
1701 input: "v: 2 >= 5",
1702 want: ">= 2 5",
1703 }, {
1704 input: "v: 2 =~ 5",
1705 want: "=~ 2 5",
1706 }, {
1707 input: "v: 2 !~ 5",
1708 want: "!~ 2 5",
1709 }, {
1710 input: "v: 2 + 5",
1711 want: "+ 2 5",
1712 }, {
1713 input: "v: 2 - 5",
1714 want: "- 2 5",
1715 }, {
1716 input: "v: 2 * 5",
1717 want: "* 2 5",
1718 }, {
1719 input: "v: 2 / 5",
1720 want: "/ 2 5",
1721 }, {
1722 input: "v: 2 % 5",
1723 want: "% 2 5",
1724 }, {
1725 input: "v: 2 quo 5",
1726 want: "quo 2 5",
1727 }, {
1728 input: "v: 2 rem 5",
1729 want: "rem 2 5",
1730 }, {
1731 input: "v: 2 div 5",
1732 want: "div 2 5",
1733 }, {
1734 input: "v: 2 mod 5",
1735 want: "mod 2 5",
1736 }, {
1737 input: "v: a.b, a b: 4",
1738 want: `. <0>.a "b"`,
1739 }, {
1740 input: `v: a["b"], a b: 3 `,
1741 want: `[] <0>.a "b"`,
1742 }, {
1743 input: "v: a[2:5], a: [1, 2, 3, 4, 5]",
1744 want: "[:] <0>.a 2 5",
1745 }, {
1746 input: "v: len([])",
1747 want: "() builtin:len []",
1748 }, {
1749 input: `v: "Hello, \(x)! Welcome to \(place)", place: string, x: string`,
1750 want: `\() "Hello, " <0>.x "! Welcome to " <0>.place ""`,
1751 }}
1752 for _, tc := range testCases {
1753 t.Run(tc.input, func(t *testing.T) {
1754 v := getInstance(t, tc.input).Lookup("v")
1755 op, operands := v.Expr()
1756 got := opToString[op]
1757 for _, v := range operands {
1758 got += " "
1759 got += debugStr(v.ctx(), v.path.v)
1760 }
1761 if got != tc.want {
1762 t.Errorf("\n got %v;\nwant %v", got, tc.want)
1763 }
1764 })
1765 }
1766}
1767
1768var opToString = map[Op]string{
1769 AndOp: "&",
1770 OrOp: "|",
1771 BooleanAndOp: "&&",
1772 BooleanOrOp: "||",
1773 EqualOp: "==",
1774 NotOp: "!",
1775 NotEqualOp: "!=",
1776 LessThanOp: "<",
1777 LessThanEqualOp: "<=",
1778 GreaterThanOp: ">",
1779 GreaterThanEqualOp: ">=",
1780 RegexMatchOp: "=~",
1781 NotRegexMatchOp: "!~",
1782 AddOp: "+",
1783 SubtractOp: "-",
1784 MultiplyOp: "*",
1785 FloatQuotientOp: "/",
1786 FloatRemainOp: "%",
1787 IntQuotientOp: "quo",
1788 IntRemainderOp: "rem",
1789 IntDivideOp: "div",
1790 IntModuloOp: "mod",
1791
1792 SelectorOp: ".",
1793 IndexOp: "[]",
1794 SliceOp: "[:]",
1795 CallOp: "()",
1796 InterpolationOp: `\()`,
1797}