1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package cue
16
17 import (
18 "bytes"
19 "fmt"
20 "io"
21 "math"
22 "math/big"
23 "reflect"
24 "strconv"
25 "strings"
26 "testing"
27
28 "github.com/google/go-cmp/cmp"
29
30 "cuelang.org/go/cue/ast"
31 "cuelang.org/go/internal/astinternal"
32 "cuelang.org/go/internal/core/adt"
33 "cuelang.org/go/internal/core/debug"
34 "cuelang.org/go/internal/core/runtime"
35 "cuelang.org/go/internal/cuetest"
36 "cuelang.org/go/internal/tdtest"
37 )
38
39 func getInstance(t *testing.T, body string) *Instance {
40 t.Helper()
41
42 var r Runtime
43
44 inst, err := r.Compile("foo", body)
45 if err != nil {
46 t.Fatalf("unexpected parse error: %v", err)
47 }
48 return inst
49 }
50
51 func TestAPI(t *testing.T) {
52 testCases := []struct {
53 input string
54 fun func(i *Instance) Value
55 want string
56 skip bool
57 }{{
58
59 input: `
60 #runSpec: {action: foo: int}
61
62 v: {ction: foo: 1}
63 `,
64 fun: func(i *Instance) Value {
65 runSpec := i.LookupDef("#runSpec")
66 v := i.Lookup("v")
67 res := runSpec.Unify(v)
68 return res
69 },
70 want: "_|_ // #runSpec.ction: field not allowed",
71 }, {
72
73 input: `
74 #runSpec: {action: foo: int}
75
76 v: {action: Foo: 1}
77 `,
78 fun: func(i *Instance) Value {
79 runSpec := i.LookupDef("#runSpec")
80 v := i.Lookup("v")
81 res := runSpec.Unify(v)
82 return res
83 },
84 want: "_|_ // #runSpec.action.Foo: field not allowed",
85 }, {
86 input: `
87 #runSpec: v: {action: foo: int}
88
89 w: {ction: foo: 1}
90 `,
91 fun: func(i *Instance) Value {
92 runSpec := i.LookupDef("#runSpec")
93 v := runSpec.Lookup("v")
94 w := i.Lookup("w")
95 res := w.Unify(v)
96 return res
97 },
98 want: "_|_ // w.ction: field not allowed",
99 }, {
100
101 input: `
102 #Steps: {
103 ...
104 }
105
106 test: #Steps & {
107 if true {
108 test1: "test1"
109 }
110 if false {
111 test2: "test2"
112 }
113 }
114 `,
115
116 fun: func(i *Instance) (val Value) {
117 v := i.Value()
118
119 sub := v.LookupPath(ParsePath("test"))
120 st, err := sub.Struct()
121 if err != nil {
122 panic(err)
123 }
124
125 for i := 0; i < st.Len(); i++ {
126 val = st.Field(i).Value
127 }
128
129 return val
130 },
131 want: `"test1"`,
132 }}
133 for _, tc := range testCases {
134 if tc.skip {
135 continue
136 }
137 t.Run("", func(t *testing.T) {
138 var r Runtime
139 inst, err := r.Compile("in", tc.input)
140 if err != nil {
141 t.Fatal(err)
142 }
143 v := tc.fun(inst)
144 got := fmt.Sprintf("%+v", v)
145 if got != tc.want {
146 t.Errorf("got:\n%s\nwant:\n%s", got, tc.want)
147 }
148 })
149 }
150 }
151
152 func TestValueType(t *testing.T) {
153 testCases := []struct {
154 value string
155 kind Kind
156 incompleteKind Kind
157 json string
158 valid bool
159 concrete bool
160 closed bool
161
162 }{{
163 value: `v: _`,
164 kind: BottomKind,
165 incompleteKind: TopKind,
166 }, {
167 value: `v: _|_`,
168 kind: BottomKind,
169 incompleteKind: BottomKind,
170 concrete: true,
171 }, {
172 value: `v: 1&2`,
173 kind: BottomKind,
174 incompleteKind: BottomKind,
175 concrete: true,
176 }, {
177 value: `v: b, b: 1&2`,
178 kind: BottomKind,
179 incompleteKind: BottomKind,
180 concrete: true,
181 }, {
182 value: `v: (b[a]), b: 1, a: 1`,
183 kind: BottomKind,
184 incompleteKind: BottomKind,
185 concrete: true,
186 }, {
187 value: `v: (b)
188 b: bool`,
189 kind: BottomKind,
190 incompleteKind: BoolKind,
191 }, {
192 value: `v: ([][b]), b: "d"`,
193 kind: BottomKind,
194 incompleteKind: BottomKind,
195 concrete: true,
196 }, {
197 value: `v: null`,
198 kind: NullKind,
199 incompleteKind: NullKind,
200 concrete: true,
201 }, {
202 value: `v: true`,
203 kind: BoolKind,
204 incompleteKind: BoolKind,
205 concrete: true,
206 }, {
207 value: `v: false`,
208 kind: BoolKind,
209 incompleteKind: BoolKind,
210 concrete: true,
211 }, {
212 value: `v: bool`,
213 kind: BottomKind,
214 incompleteKind: BoolKind,
215 }, {
216 value: `v: 2`,
217 kind: IntKind,
218 incompleteKind: IntKind,
219 concrete: true,
220 }, {
221 value: `v: 2.0`,
222 kind: FloatKind,
223 incompleteKind: FloatKind,
224 concrete: true,
225 }, {
226 value: `v: 2.0Mi`,
227 kind: IntKind,
228 incompleteKind: IntKind,
229 concrete: true,
230 }, {
231 value: `v: 14_000`,
232 kind: IntKind,
233 incompleteKind: IntKind,
234 concrete: true,
235 }, {
236 value: `v: >=0 & <5`,
237 kind: BottomKind,
238 incompleteKind: NumberKind,
239 }, {
240 value: `v: float`,
241 kind: BottomKind,
242 incompleteKind: FloatKind,
243 }, {
244 value: `v: "str"`,
245 kind: StringKind,
246 incompleteKind: StringKind,
247 concrete: true,
248 }, {
249 value: "v: '''\n'''",
250 kind: BytesKind,
251 incompleteKind: BytesKind,
252 concrete: true,
253 }, {
254 value: "v: string",
255 kind: BottomKind,
256 incompleteKind: StringKind,
257 }, {
258 value: `v: {}`,
259 kind: StructKind,
260 incompleteKind: StructKind,
261 concrete: true,
262 }, {
263 value: `v: close({})`,
264 kind: StructKind,
265 incompleteKind: StructKind,
266 concrete: true,
267 closed: true,
268 }, {
269 value: `v: []`,
270 kind: ListKind,
271 incompleteKind: ListKind,
272 concrete: true,
273 closed: true,
274 }, {
275 value: `v: [...int]`,
276 kind: BottomKind,
277 incompleteKind: ListKind,
278 concrete: false,
279 }, {
280 value: `v: {a: int, b: [1][a]}.b`,
281 kind: BottomKind,
282 concrete: false,
283 }, {
284 value: `import "time"
285 v: time.Time`,
286 kind: BottomKind,
287 incompleteKind: StringKind,
288 concrete: false,
289 }, {
290 value: `import "time"
291 v: {a: time.Time}.a`,
292 kind: BottomKind,
293 incompleteKind: StringKind,
294 concrete: false,
295 }, {
296 value: `import "time"
297 v: {a: time.Time & string}.a`,
298 kind: BottomKind,
299 incompleteKind: StringKind,
300 concrete: false,
301 }, {
302 value: `import "strings"
303 v: {a: strings.ContainsAny("D")}.a`,
304 kind: BottomKind,
305 incompleteKind: StringKind,
306 concrete: false,
307 }, {
308 value: `import "struct"
309 v: {a: struct.MaxFields(2) & {}}.a`,
310 kind: StructKind,
311 incompleteKind: StructKind,
312 concrete: true,
313 }, {
314 value: `v: #Foo
315 #Foo: {
316 name: string,
317 ...
318 }`,
319 kind: StructKind,
320 incompleteKind: StructKind,
321 concrete: true,
322 }, {
323 value: `v: #Foo
324 #Foo: {
325 name: string,
326 }`,
327 kind: StructKind,
328 incompleteKind: StructKind,
329 concrete: true,
330 closed: true,
331 }, {
332 value: `v: #Foo | int
333 #Foo: {
334 name: string,
335 }`,
336 incompleteKind: StructKind | IntKind,
337
338
339 closed: false,
340 }}
341 for _, tc := range testCases {
342 t.Run(tc.value, func(t *testing.T) {
343 inst := getInstance(t, tc.value)
344 v := inst.Lookup("v")
345 if got := v.Kind(); got != tc.kind {
346 t.Errorf("Kind: got %x; want %x", int(got), int(tc.kind))
347 }
348 want := tc.incompleteKind | BottomKind
349 if got := v.IncompleteKind(); got != want {
350 t.Errorf("IncompleteKind: got %x; want %x", int(got), int(want))
351 }
352 if got := v.IsConcrete(); got != tc.concrete {
353 t.Errorf("IsConcrete: got %v; want %v", got, tc.concrete)
354 }
355 if got := v.IsClosed(); got != tc.closed {
356 t.Errorf("IsClosed: got %v; want %v", got, tc.closed)
357 }
358 })
359 }
360 }
361
362 func TestInt(t *testing.T) {
363 testCases := []struct {
364 value string
365 int int64
366 uint uint64
367 base int
368 err string
369 errU string
370 notInt bool
371 }{{
372 value: "1",
373 int: 1,
374 uint: 1,
375 }, {
376 value: "-1",
377 int: -1,
378 uint: 0,
379 errU: ErrAbove.Error(),
380 }, {
381 value: "-111222333444555666777888999000",
382 int: math.MinInt64,
383 uint: 0,
384 err: ErrAbove.Error(),
385 errU: ErrAbove.Error(),
386 }, {
387 value: "111222333444555666777888999000",
388 int: math.MaxInt64,
389 uint: math.MaxUint64,
390 err: ErrBelow.Error(),
391 errU: ErrBelow.Error(),
392 }, {
393 value: "1.0",
394 err: "cannot use value 1.0 (type float) as int",
395 errU: "cannot use value 1.0 (type float) as int",
396 notInt: true,
397 }, {
398 value: "int",
399 err: "non-concrete value int",
400 errU: "non-concrete value int",
401 notInt: true,
402 }, {
403 value: "_|_",
404 err: "explicit error (_|_ literal) in source",
405 errU: "explicit error (_|_ literal) in source",
406 notInt: true,
407 }}
408 for _, tc := range testCases {
409 t.Run(tc.value, func(t *testing.T) {
410 n := getInstance(t, tc.value).Value()
411 base := 10
412 if tc.base > 0 {
413 base = tc.base
414 }
415 b, err := n.AppendInt(nil, base)
416 if checkFailed(t, err, tc.err, "append") {
417 want := tc.value
418 if got := string(b); got != want {
419 t.Errorf("append: got %v; want %v", got, want)
420 }
421 }
422
423 vi, err := n.Int64()
424 checkErr(t, err, tc.err, "Int64")
425 if vi != tc.int {
426 t.Errorf("Int64: got %v; want %v", vi, tc.int)
427 }
428
429 vu, err := n.Uint64()
430 checkErr(t, err, tc.errU, "Uint64")
431 if vu != uint64(tc.uint) {
432 t.Errorf("Uint64: got %v; want %v", vu, tc.uint)
433 }
434 })
435 }
436 }
437
438 func TestFloat(t *testing.T) {
439 testCases := []struct {
440 value string
441 float string
442 float64 float64
443 mant string
444 exp int
445 fmt byte
446 prec int
447 kind Kind
448 err string
449 }{{
450 value: "1",
451 float: "1",
452 mant: "1",
453 exp: 0,
454 float64: 1,
455 fmt: 'g',
456 kind: IntKind,
457 }, {
458 value: "-1",
459 float: "-1",
460 mant: "-1",
461 exp: 0,
462 float64: -1,
463 fmt: 'g',
464 kind: IntKind,
465 }, {
466 value: "0.0",
467 float: "0.0",
468 mant: "0",
469 exp: -1,
470 float64: 0.0,
471 fmt: 'g',
472 kind: FloatKind,
473 }, {
474 value: "1.0",
475 float: "1.0",
476 mant: "10",
477 exp: -1,
478 float64: 1.0,
479 fmt: 'g',
480 kind: FloatKind,
481 }, {
482 value: "2.6",
483 float: "2.6",
484 mant: "26",
485 exp: -1,
486 float64: 2.6,
487 fmt: 'g',
488 kind: FloatKind,
489 }, {
490 value: "20.600",
491 float: "20.60",
492 mant: "20600",
493 exp: -3,
494 float64: 20.60,
495 prec: 2,
496 fmt: 'f',
497 kind: FloatKind,
498 }, {
499 value: "1/0",
500 float: "",
501 float64: 0,
502 prec: 2,
503 fmt: 'f',
504 err: "division by zero",
505 kind: BottomKind,
506 }, {
507 value: "1.797693134862315708145274237317043567982e+308",
508 float: "1.8e+308",
509 mant: "1797693134862315708145274237317043567982",
510 exp: 269,
511 float64: math.Inf(1),
512 prec: 2,
513 fmt: 'g',
514 err: ErrAbove.Error(),
515 kind: FloatKind,
516 }, {
517 value: "-1.797693134862315708145274237317043567982e+308",
518 float: "-1.8e+308",
519 mant: "-1797693134862315708145274237317043567982",
520 exp: 269,
521 float64: math.Inf(-1),
522 prec: 2,
523 fmt: 'g',
524 kind: FloatKind,
525 err: ErrBelow.Error(),
526 }, {
527 value: "4.940656458412465441765687928682213723650e-324",
528 float: "4.941e-324",
529 mant: "4940656458412465441765687928682213723650",
530 exp: -363,
531 float64: 0,
532 prec: 4,
533 fmt: 'g',
534 kind: FloatKind,
535 err: ErrBelow.Error(),
536 }, {
537 value: "-4.940656458412465441765687928682213723650e-324",
538 float: "-4.940656458412465441765687928682213723650e-324",
539 mant: "-4940656458412465441765687928682213723650",
540 exp: -363,
541 float64: 0,
542 prec: -1,
543 fmt: 'g',
544 kind: FloatKind,
545 err: ErrAbove.Error(),
546 }}
547 for _, tc := range testCases {
548 t.Run(tc.value, func(t *testing.T) {
549 n := getInstance(t, tc.value).Value()
550 if n.Kind() != tc.kind {
551 t.Fatal("Not a number")
552 }
553
554 var mant big.Int
555 exp, err := n.MantExp(&mant)
556 mstr := ""
557 if err == nil {
558 mstr = mant.String()
559 }
560 if exp != tc.exp || mstr != tc.mant {
561 t.Errorf("mantExp: got %s %d; want %s %d", mstr, exp, tc.mant, tc.exp)
562 }
563
564 b, _ := n.AppendFloat(nil, tc.fmt, tc.prec)
565 want := tc.float
566 if got := string(b); got != want {
567 t.Errorf("append: got %v; want %v", got, want)
568 }
569
570 f, err := n.Float64()
571 checkErr(t, err, tc.err, "Float64")
572 if f != tc.float64 {
573 t.Errorf("Float64: got %v; want %v", f, tc.float64)
574 }
575 })
576 }
577 }
578
579 func TestString(t *testing.T) {
580 testCases := []struct {
581 value string
582 str string
583 err string
584 }{{
585 value: `""`,
586 str: ``,
587 }, {
588 value: `"Hello world!"`,
589 str: `Hello world!`,
590 }, {
591 value: `"Hello \(#world)!"
592 #world: "world"`,
593 str: `Hello world!`,
594 }, {
595 value: `string`,
596 err: "non-concrete value string",
597 }}
598 for _, tc := range testCases {
599 t.Run(tc.value, func(t *testing.T) {
600 str, err := getInstance(t, tc.value).Value().String()
601 checkFatal(t, err, tc.err, "init")
602 if str != tc.str {
603 t.Errorf("String: got %q; want %q", str, tc.str)
604 }
605
606 b, err := getInstance(t, tc.value).Value().Bytes()
607 checkFatal(t, err, tc.err, "init")
608 if got := string(b); got != tc.str {
609 t.Errorf("Bytes: got %q; want %q", got, tc.str)
610 }
611
612 r, err := getInstance(t, tc.value).Value().Reader()
613 checkFatal(t, err, tc.err, "init")
614 b, _ = io.ReadAll(r)
615 if got := string(b); got != tc.str {
616 t.Errorf("Reader: got %q; want %q", got, tc.str)
617 }
618 })
619 }
620 }
621
622 func TestError(t *testing.T) {
623 testCases := []struct {
624 value string
625 err string
626 }{{
627 value: `_|_`,
628 err: "explicit error (_|_ literal) in source",
629 }, {
630 value: `"Hello world!"`,
631 }, {
632 value: `string`,
633 err: "",
634 }}
635 for _, tc := range testCases {
636 t.Run(tc.value, func(t *testing.T) {
637 err := getInstance(t, tc.value).Value().Err()
638 checkErr(t, err, tc.err, "init")
639 })
640 }
641 }
642
643 func TestNull(t *testing.T) {
644 testCases := []struct {
645 value string
646 err string
647 }{{
648 value: `v: _|_`,
649 err: "explicit error (_|_ literal) in source",
650 }, {
651 value: `v: "str"`,
652 err: "cannot use value \"str\" (type string) as null",
653 }, {
654 value: `v: null`,
655 }, {
656 value: `v: _`,
657 err: "non-concrete value _",
658 }}
659 for _, tc := range testCases {
660 t.Run(tc.value, func(t *testing.T) {
661 err := getInstance(t, tc.value).Lookup("v").Null()
662 checkErr(t, err, tc.err, "init")
663 })
664 }
665 }
666
667 func TestBool(t *testing.T) {
668 testCases := []struct {
669 value string
670 bool bool
671 err string
672 }{{
673 value: `_|_`,
674 err: "explicit error (_|_ literal) in source",
675 }, {
676 value: `"str"`,
677 err: "cannot use value \"str\" (type string) as bool",
678 }, {
679 value: `true`,
680 bool: true,
681 }, {
682 value: `false`,
683 }, {
684 value: `bool`,
685 err: "non-concrete value bool",
686 }}
687 for _, tc := range testCases {
688 t.Run(tc.value, func(t *testing.T) {
689 got, err := getInstance(t, tc.value).Value().Bool()
690 if checkErr(t, err, tc.err, "init") {
691 if got != tc.bool {
692 t.Errorf("got %v; want %v", got, tc.bool)
693 }
694 }
695 })
696 }
697 }
698
699 func TestList(t *testing.T) {
700 testCases := []struct {
701 value string
702 res string
703 err string
704 }{{
705 value: `_|_`,
706 err: "explicit error (_|_ literal) in source",
707 }, {
708 value: `"str"`,
709 err: "cannot use value \"str\" (type string) as list",
710 }, {
711 value: `[]`,
712 res: "[]",
713 }, {
714 value: `[1,2,3]`,
715 res: "[1,2,3,]",
716 }, {
717 value: `[for x in #y if x > 1 { x }]
718 #y: [1,2,3]`,
719 res: "[2,3,]",
720 }, {
721 value: `[int]`,
722 err: "cannot convert incomplete value",
723 }}
724 for _, tc := range testCases {
725 t.Run(tc.value, func(t *testing.T) {
726 l, err := getInstance(t, tc.value).Value().List()
727 checkFatal(t, err, tc.err, "init")
728
729 buf := []byte{'['}
730 for wantIdx := 0; l.Next(); wantIdx++ {
731
732 if got := l.Selector().Index(); got != wantIdx {
733 t.Errorf("Index got %v; want %v", got, wantIdx)
734 }
735 b, err := l.Value().MarshalJSON()
736 checkFatal(t, err, tc.err, "list.Value")
737 buf = append(buf, b...)
738 buf = append(buf, ',')
739 }
740 buf = append(buf, ']')
741 if got := string(buf); got != tc.res {
742 t.Errorf("got %v; want %v", got, tc.res)
743 }
744 })
745 }
746 }
747
748 func TestFields(t *testing.T) {
749 testCases := []struct {
750 value string
751 res string
752 err string
753 opts []Option
754 }{{
755 value: `{ #def: 1, _hidden: 2, opt?: 3, reg: 4 }`,
756 res: "{reg:4,}",
757 }, {
758 value: `_|_`,
759 err: "explicit error (_|_ literal) in source",
760 }, {
761 value: `"str"`,
762 err: "cannot use value \"str\" (type string) as struct",
763 }, {
764 value: `{}`,
765 res: "{}",
766 }, {
767 value: `{a:1,b:2,c:3}`,
768 res: "{a:1,b:2,c:3,}",
769 }, {
770 value: `{a:1,"_b":2,c:3,_d:4}`,
771 res: `{a:1,"_b":2,c:3,}`,
772 }, {
773 value: `{_a:"a"}`,
774 res: "{}",
775 }, {
776 value: `{ for k, v in #y if v > 1 {"\(k)": v} }
777 #y: {a:1,b:2,c:3}`,
778 res: "{b:2,c:3,}",
779 }, {
780 value: `{ #def: 1, _hidden: 2, opt?: 3, reg: 4 }`,
781 res: "{reg:4,}",
782 }, {
783 value: `{a:1,b:2,c:int}`,
784 err: "cannot convert incomplete value",
785 }, {
786 value: `
787 step1: {}
788 step2: {prefix: 3}
789 if step2.value > 100 {
790 step3: {prefix: step2.value}
791 }
792 _hidden: 3`,
793 res: `{step1:{},step2:{"prefix":3},}`,
794 }, {
795 opts: []Option{Final()},
796 value: `
797 step1: {}
798 if step1.value > 100 {
799 }`,
800 err: "undefined field: value",
801 }, {
802 opts: []Option{Concrete(true)},
803 value: `
804 step1: {}
805 if step1.value > 100 {
806 }`,
807 err: "undefined field: value",
808 }, {
809 value: `{a!: 1, b?: 2, c: 3}`,
810 err: "a: field is required but not present",
811 }, {
812 opts: []Option{Hidden(true)},
813 value: `1, _a: 2`,
814 res: `{_a:2,}`,
815 }, {
816 opts: []Option{Definitions(true)},
817 value: `1, #a: 2`,
818 res: `{#a:2,}`,
819 }, {
820 opts: []Option{Optional(true)},
821 value: `1, a?: 2`,
822 err: "cannot use value 1 (type int) as struct",
823 }}
824 for _, tc := range testCases {
825 t.Run(tc.value, func(t *testing.T) {
826 obj := getInstance(t, tc.value).Value()
827
828 iter, err := obj.Fields(tc.opts...)
829 checkFatal(t, err, tc.err, "init")
830
831 buf := []byte{'{'}
832 for iter.Next() {
833 buf = append(buf, iter.Selector().String()...)
834 buf = append(buf, ':')
835 b, err := iter.Value().MarshalJSON()
836 checkFatal(t, err, tc.err, "Obj.At")
837 buf = append(buf, b...)
838 buf = append(buf, ',')
839 }
840 buf = append(buf, '}')
841 if got := string(buf); got != tc.res {
842 t.Errorf("got %v; want %v", got, tc.res)
843 }
844
845 iter, _ = obj.Fields(tc.opts...)
846 for iter.Next() {
847 want, err := iter.Value().MarshalJSON()
848 checkFatal(t, err, tc.err, "Obj.At2")
849
850 got, err := obj.LookupPath(MakePath(iter.Selector())).MarshalJSON()
851 checkFatal(t, err, tc.err, "Obj.At2")
852
853 if !bytes.Equal(got, want) {
854 t.Errorf("Lookup: got %q; want %q", got, want)
855 }
856 }
857 v := obj.LookupPath(MakePath(Str("non-existing")))
858 checkErr(t, v.Err(), "not found", "non-existing")
859 })
860 }
861 }
862
863 func TestAllFields(t *testing.T) {
864 testCases := []struct {
865 value string
866 res string
867 err string
868 }{{
869 value: `{a:1,"_b":2,c:3,_d:4}`,
870 res: `{a:1,"_b":2,c:3,_d:4,}`,
871 }, {
872 value: `{_a:"a"}`,
873 res: `{_a:"a",}`,
874 }, {
875 value: `{_a:"a", b?: "b", #c: 3}`,
876 res: `{_a:"a",b?:"b",#c:3,}`,
877 }, {
878
879 value: `{a: 1, if false { b: 2 }}`,
880 res: `{a:1,}`,
881 }, {
882 value: `{a!:1,b?:2,c:3}`,
883 res: `{a!:1,b?:2,c:3,}`,
884 }}
885 for _, tc := range testCases {
886 t.Run(tc.value, func(t *testing.T) {
887 obj := getInstance(t, tc.value).Value()
888
889 var iter *Iterator
890 iter, err := obj.Fields(All())
891 checkFatal(t, err, tc.err, "init")
892
893 buf := []byte{'{'}
894 for iter.Next() {
895 buf = append(buf, iter.Selector().String()...)
896 buf = append(buf, ':')
897 b, err := iter.Value().MarshalJSON()
898 checkFatal(t, err, tc.err, "Obj.At")
899 buf = append(buf, b...)
900 buf = append(buf, ',')
901 }
902 buf = append(buf, '}')
903 if got := string(buf); got != tc.res {
904 t.Errorf("got %v; want %v", got, tc.res)
905 }
906 })
907 }
908 }
909
910 func TestFieldType(t *testing.T) {
911 testCases := []struct {
912 value string
913 want string
914 }{{
915 value: `{a:1,"_b":2,c:3,_d:4,#def: 1}`,
916 want: `
917 StringLabel
918 StringLabel
919 StringLabel
920 HiddenLabel
921 DefinitionLabel`,
922 }, {
923 value: `{a!:1,b?:2,c:3}`,
924 want: `
925 StringLabel|RequiredConstraint
926 StringLabel|OptionalConstraint
927 StringLabel`,
928 }}
929 for _, tc := range testCases {
930 t.Run(tc.value, func(t *testing.T) {
931 obj := getInstance(t, tc.value).Value()
932
933 iter, err := obj.Fields(All())
934 if err != nil {
935 t.Fatal(err)
936 }
937
938 b := &strings.Builder{}
939 for iter.Next() {
940 fmt.Fprint(b, "\n\t\t", iter.FieldType())
941 }
942 if got := b.String(); got != tc.want {
943 t.Errorf("got:%v\nwant:%v", got, tc.want)
944 }
945 })
946 }
947 }
948
949 func TestLookup(t *testing.T) {
950 var runtime = new(Runtime)
951 inst, err := runtime.Compile("x.cue", `
952 #V: {
953 x: int
954 }
955 #X: {
956 [string]: int64
957 } & #V
958 v: #X
959
960 a: {
961 b!: 1
962 c: 2
963 }
964 `)
965 if err != nil {
966 t.Fatalf("compile: %v", err)
967 }
968
969
970
971
972
973
974 type testCase struct {
975 ref []string
976 result string
977 syntax string
978 }
979 testCases := []testCase{{
980 ref: []string{"a"},
981 result: `{
982 b!: 1
983 c: 2
984 }`,
985 syntax: "{b!: 1, c: 2}",
986 }, {
987
988 ref: []string{"a", "c"},
989 result: "2",
990 syntax: "2",
991 }, {
992 ref: []string{"a", "b"},
993 result: "_|_ // a.b: field is required but not present",
994 syntax: "1",
995 }, {
996 ref: []string{"v", "x"},
997 result: "int64",
998 syntax: "int64",
999 }}
1000 for _, tc := range testCases {
1001 t.Run("", func(t *testing.T) {
1002 v := inst.Lookup(tc.ref...)
1003
1004 if got := fmt.Sprintf("%+v", v); got != tc.result {
1005 t.Errorf("got %v; want %v", got, tc.result)
1006 }
1007
1008 got := fmt.Sprint(astinternal.DebugStr(v.Eval().Syntax()))
1009 if got != tc.syntax {
1010 t.Errorf("got %v; want %v", got, tc.syntax)
1011 }
1012
1013 v = inst.Lookup()
1014 for _, ref := range tc.ref {
1015 s, err := v.Struct()
1016 if err != nil {
1017 t.Fatal(err)
1018 }
1019 fi, err := s.FieldByName(ref, false)
1020 if err != nil {
1021 t.Fatal(err)
1022 }
1023 v = fi.Value
1024
1025
1026
1027 if v.v.ArcType != adt.ArcMember {
1028 return
1029 }
1030 }
1031
1032 if got := fmt.Sprintf("%+v", v); got != tc.result {
1033 t.Errorf("got %v; want %v", got, tc.result)
1034 }
1035
1036 got = fmt.Sprint(astinternal.DebugStr(v.Eval().Syntax()))
1037 if got != tc.syntax {
1038 t.Errorf("got %v; want %v", got, tc.syntax)
1039 }
1040 })
1041 }
1042 }
1043
1044 func compileT(t *testing.T, r *Runtime, s string) *Instance {
1045 t.Helper()
1046 inst, err := r.Compile("", s)
1047 if err != nil {
1048 t.Fatal(err)
1049 }
1050 return inst
1051 }
1052
1053 func goValue(v Value) interface{} {
1054 var x interface{}
1055 err := v.Decode(&x)
1056 if err != nil {
1057 return err
1058 }
1059 return x
1060 }
1061
1062
1063 func TestFill(t *testing.T) {
1064 r := &Runtime{}
1065
1066 inst, err := r.CompileExpr(ast.NewStruct("bar", ast.NewString("baz")))
1067 if err != nil {
1068 t.Fatal(err)
1069 }
1070
1071 testCases := []struct {
1072 in string
1073 x interface{}
1074 path string
1075 out string
1076 }{{
1077 in: `
1078 foo: int
1079 bar: foo
1080 `,
1081 x: 3,
1082 path: "foo",
1083 out: `
1084 foo: 3
1085 bar: 3
1086 `,
1087 }, {
1088 in: `
1089 string
1090 `,
1091 x: "foo",
1092 path: "",
1093 out: `
1094 "foo"
1095 `,
1096 }, {
1097 in: `
1098 foo: _
1099 `,
1100 x: inst.Value(),
1101 path: "foo",
1102 out: `
1103 {foo: {bar: "baz"}}
1104 `,
1105 }}
1106
1107 for _, tc := range testCases {
1108 var path []string
1109 if tc.path != "" {
1110 path = strings.Split(tc.path, ",")
1111 }
1112
1113 v := compileT(t, r, tc.in).Value()
1114 v = v.Fill(tc.x, path...)
1115
1116 w := compileT(t, r, tc.out).Value()
1117
1118 if diff := cmp.Diff(goValue(v), goValue(w)); diff != "" {
1119 t.Error(diff)
1120 t.Errorf("\ngot: %s\nwant: %s", v, w)
1121 }
1122 }
1123 }
1124
1125 func TestFill2(t *testing.T) {
1126 r := &Runtime{}
1127
1128 root, err := r.Compile("test", `
1129 #Provider: {
1130 ID: string
1131 notConcrete: bool
1132 a: int
1133 b: int
1134 }
1135 `)
1136
1137 if err != nil {
1138 t.Fatal(err)
1139 }
1140
1141 spec := root.LookupDef("#Provider")
1142 providerInstance := spec.Fill("12345", "ID")
1143 root, err = root.Fill(providerInstance, "providers", "myprovider")
1144 if err != nil {
1145 t.Fatal(err)
1146 }
1147
1148 got := fmt.Sprintf("%#v", root.Value())
1149 want := `#Provider: {
1150 ID: string
1151 notConcrete: bool
1152 a: int
1153 b: int
1154 }
1155 providers: {
1156 myprovider: {
1157 ID: "12345"
1158 notConcrete: bool
1159 a: int
1160 b: int
1161 }
1162 }`
1163 if got != want {
1164 t.Errorf("got: %s\nwant: %s", got, want)
1165 }
1166 }
1167
1168 func TestFillPath(t *testing.T) {
1169 r := &Runtime{}
1170
1171 inst, err := r.CompileExpr(ast.NewStruct("bar", ast.NewString("baz")))
1172 if err != nil {
1173 t.Fatal(err)
1174 }
1175
1176 testCases := []struct {
1177 in string
1178 x interface{}
1179 path Path
1180 out string
1181 }{{
1182 in: `
1183 foo: int
1184 bar: foo
1185 `,
1186 x: 3,
1187 path: ParsePath("foo"),
1188 out: `
1189 foo: 3
1190 bar: 3
1191 `,
1192 }, {
1193 in: `
1194 X="#foo": int
1195 bar: X
1196 `,
1197 x: 3,
1198 path: ParsePath(`"#foo"`),
1199 out: `
1200 "#foo": 3
1201 bar: 3
1202 `,
1203 }, {
1204 in: `
1205 X="#foo": foo: int
1206 bar: X.foo
1207 `,
1208 x: 3,
1209 path: ParsePath(`"#foo".foo`),
1210 out: `
1211 "#foo": foo: 3
1212 bar: 3
1213 `,
1214 }, {
1215 in: `
1216 foo: #foo: int
1217 bar: foo.#foo
1218 `,
1219 x: 3,
1220 path: ParsePath("foo.#foo"),
1221 out: `
1222 foo: {
1223 #foo: 3
1224 }
1225 bar: 3
1226 `,
1227 }, {
1228 in: `
1229 foo: _foo: int
1230 bar: foo._foo
1231 `,
1232 x: 3,
1233 path: MakePath(Str("foo"), Hid("_foo", "_")),
1234 out: `
1235 foo: {
1236 _foo: 3
1237 }
1238 bar: 3
1239 `,
1240 }, {
1241 in: `
1242 string
1243 `,
1244 x: "foo",
1245 path: ParsePath(""),
1246 out: `
1247 "foo"
1248 `,
1249 }, {
1250 in: `
1251 foo: _
1252 `,
1253 x: inst.Value(),
1254 path: ParsePath("foo"),
1255 out: `
1256 {foo: {bar: "baz"}}
1257 `,
1258 }, {
1259
1260 in: `
1261 foo: _
1262 x: 1
1263 `,
1264 x: ast.NewIdent("x"),
1265 path: ParsePath("foo"),
1266 out: `
1267 {foo: 1, x: 1}
1268 `,
1269 }, {
1270 in: `
1271 foo: {
1272 bar: _
1273 x: 1
1274 }
1275 `,
1276 x: ast.NewIdent("x"),
1277 path: ParsePath("foo.bar"),
1278 out: `
1279 {foo: {bar: 1, x: 1}}
1280 `,
1281 }, {
1282
1283 in: `
1284 x: 1
1285 foo: {
1286 bar: _
1287 }
1288 `,
1289 x: ast.NewIdent("x"),
1290 path: ParsePath("foo.bar"),
1291 out: `
1292 {foo: {bar: 1}, x: 1}
1293 `,
1294 }, {
1295
1296 in: `
1297 foo: {
1298 bar: _
1299 }
1300 `,
1301 x: ast.NewStruct(
1302 ast.NewIdent("x"), ast.NewString("1"),
1303 ast.NewIdent("y"), ast.NewIdent("x"),
1304 ),
1305 path: ParsePath("foo.bar"),
1306 out: `
1307 {foo: {bar: {x: "1", y: "1"}}}
1308 `,
1309 }, {
1310
1311 in: `
1312 foo: x: 1
1313 `,
1314 x: ast.NewIdent("x"),
1315 path: ParsePath("foo.bar.baz"),
1316 out: `
1317 {foo: {x: 1, bar: baz: 1}}
1318 `,
1319 }, {
1320
1321 in: `
1322 _
1323 #foo: 1
1324 `,
1325 x: ast.NewIdent("#foo"),
1326 out: `{1, #foo: 1}`,
1327 }, {
1328 in: `[...int]`,
1329 x: 1,
1330 path: ParsePath("0"),
1331 out: `[1]`,
1332 }, {
1333 in: `[1, ...int]`,
1334 x: 1,
1335 path: ParsePath("1"),
1336 out: `[1, 1]`,
1337 }, {
1338 in: `a: {b: v: int, c: v: int}`,
1339 x: 1,
1340 path: MakePath(Str("a"), AnyString, Str("v")),
1341 out: `{
1342 a: {
1343 b: {
1344 v: 1
1345 }
1346 c: {
1347 v: 1
1348 }
1349 }
1350 }`,
1351 }, {
1352 in: `a: [_]`,
1353 x: 1,
1354 path: MakePath(Str("a"), AnyIndex, Str("b")),
1355 out: `{
1356 a: [{
1357 b: 1
1358 }]
1359 }`,
1360 }, {
1361 in: `a: 1`,
1362 x: 1,
1363 path: MakePath(Str("b").Optional()),
1364 out: `{a: 1}`,
1365 }, {
1366 in: `b: int`,
1367 x: 1,
1368 path: MakePath(Str("b").Optional()),
1369 out: `{b: 1}`,
1370 }}
1371
1372 for _, tc := range testCases {
1373 t.Run("", func(t *testing.T) {
1374 v := compileT(t, r, tc.in).Value()
1375 v = v.FillPath(tc.path, tc.x)
1376
1377 w := compileT(t, r, tc.out).Value()
1378
1379 if diff := cmp.Diff(goValue(v), goValue(w)); diff != "" {
1380 t.Error(diff)
1381 t.Error(cmp.Diff(goValue(v), goValue(w)))
1382 t.Errorf("\ngot: %s\nwant: %s", v, w)
1383 }
1384 })
1385 }
1386 }
1387
1388 func TestFillPathError(t *testing.T) {
1389 r := &Runtime{}
1390
1391 testCases := []struct {
1392 in string
1393 x interface{}
1394 path Path
1395 err string
1396 }{{
1397
1398 in: `_`,
1399 x: make(chan int),
1400 err: "unsupported Go type (chan int)",
1401 }}
1402
1403 for _, tc := range testCases {
1404 t.Run("", func(t *testing.T) {
1405 v := compileT(t, r, tc.in).Value()
1406 v = v.FillPath(tc.path, tc.x)
1407
1408 err := v.Err()
1409 if err == nil {
1410 t.Errorf("unexpected success")
1411 }
1412
1413 if got := err.Error(); !strings.Contains(got, tc.err) {
1414 t.Errorf("\ngot: %s\nwant: %s", got, tc.err)
1415 }
1416 })
1417 }
1418 }
1419
1420 func TestAllows(t *testing.T) {
1421 r := &Runtime{}
1422
1423 testCases := []struct {
1424 desc string
1425 in string
1426 sel Selector
1427 allow bool
1428 }{{
1429 desc: "allow new field in open struct",
1430 in: `
1431 x: {
1432 a: int
1433 }
1434 `,
1435 sel: Str("b"),
1436 allow: true,
1437 }, {
1438 desc: "disallow new field in definition",
1439 in: `
1440 x: #Def
1441 #Def: {
1442 a: int
1443 }
1444 `,
1445 sel: Str("b"),
1446 }, {
1447 desc: "disallow new field in explicitly closed struct",
1448 in: `
1449 x: close({
1450 a: int
1451 })
1452 `,
1453 sel: Str("b"),
1454 }, {
1455 desc: "allow index in open list",
1456 in: `
1457 x: [...int]
1458 `,
1459 sel: Index(100),
1460 allow: true,
1461 }, {
1462 desc: "disallow index in closed list",
1463 in: `
1464 x: []
1465 `,
1466 sel: Index(0),
1467 }, {
1468 desc: "allow existing index in closed list",
1469 in: `
1470 x: [1]
1471 `,
1472 sel: Index(0),
1473 allow: true,
1474 }, {
1475 desc: "definition in non-def closed list",
1476 in: `
1477 x: [1]
1478 `,
1479 sel: Def("#foo"),
1480 allow: true,
1481 }, {
1482
1483 desc: "definition in def open list",
1484 in: `
1485 x: #Def
1486 x: [1]
1487 #Def: [...int]
1488 `,
1489 sel: Def("#foo"),
1490 allow: true,
1491 }, {
1492 desc: "field in def open list",
1493 in: `
1494 x: #Def
1495 x: [1]
1496 #Def: [...int]
1497 `,
1498 sel: Str("foo"),
1499 }, {
1500 desc: "definition in open scalar",
1501 in: `
1502 x: 1
1503 `,
1504 sel: Def("#foo"),
1505 allow: true,
1506 }, {
1507 desc: "field in scalar",
1508 in: `
1509 x: #Def
1510 x: 1
1511 #Def: int
1512 `,
1513 sel: Str("foo"),
1514 }, {
1515 desc: "any index in closed list",
1516 in: `
1517 x: [1]
1518 `,
1519 sel: AnyIndex,
1520 }, {
1521 desc: "any index in open list",
1522 in: `
1523 x: [...int]
1524 `,
1525 sel: AnyIndex,
1526 allow: true,
1527 }, {
1528 desc: "definition in open scalar",
1529 in: `
1530 x: 1
1531 `,
1532 sel: anyDefinition,
1533 allow: true,
1534 }, {
1535 desc: "field in open scalar",
1536 in: `
1537 x: 1
1538 `,
1539 sel: AnyString,
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551 }, {
1552 desc: "allow field in any",
1553 in: `
1554 x: _
1555 `,
1556 sel: AnyString,
1557 allow: true,
1558 }, {
1559 desc: "allow index in any",
1560 in: `
1561 x: _
1562 `,
1563 sel: AnyIndex,
1564 allow: true,
1565 }, {
1566 desc: "allow index in disjunction",
1567 in: `
1568 x: [...int] | 1
1569 `,
1570 sel: AnyIndex,
1571 allow: true,
1572 }, {
1573 desc: "allow index in disjunction",
1574 in: `
1575 x: [] | [...int]
1576 `,
1577 sel: AnyIndex,
1578 allow: true,
1579 }, {
1580 desc: "disallow index in disjunction",
1581 in: `
1582 x: [1, 2] | [3, 2]
1583 `,
1584 sel: AnyIndex,
1585 }, {
1586 desc: "disallow index in non-list disjunction",
1587 in: `
1588 x: "foo" | 1
1589 `,
1590 sel: AnyIndex,
1591 }, {
1592 desc: "allow label in disjunction",
1593 in: `
1594 x: {} | 1
1595 `,
1596 sel: AnyString,
1597 allow: true,
1598 }, {
1599 desc: "allow label in disjunction",
1600 in: `
1601 x: #Def
1602 #Def: { a: 1 } | { b: 1, ... }
1603 `,
1604 sel: AnyString,
1605 allow: true,
1606 }, {
1607 desc: "disallow label in disjunction",
1608 in: `
1609 x: #Def
1610 #Def: { a: 1 } | { b: 1 }
1611 `,
1612 sel: AnyString,
1613 }, {
1614 desc: "pattern constraint",
1615 in: `
1616 x: #PC
1617 #PC: [>"m"]: int
1618 `,
1619 sel: Str(""),
1620 }, {
1621 desc: "pattern constraint",
1622 in: `
1623 x: #PC
1624 #PC: [>"m"]: int
1625 `,
1626 sel: Str("z"),
1627 allow: true,
1628 }, {
1629 desc: "any in pattern constraint",
1630 in: `
1631 x: #PC
1632 #PC: [>"m"]: int
1633 `,
1634 sel: AnyString,
1635 }, {
1636 desc: "any in pattern constraint",
1637 in: `
1638 x: #PC
1639 #PC: [>" "]: int
1640 `,
1641 sel: AnyString,
1642 }}
1643
1644 path := ParsePath("x")
1645
1646 for _, tc := range testCases {
1647 t.Run(tc.desc, func(t *testing.T) {
1648 v := compileT(t, r, tc.in).Value()
1649 v = v.LookupPath(path)
1650
1651 got := v.Allows(tc.sel)
1652 if got != tc.allow {
1653 t.Errorf("got %v; want %v", got, tc.allow)
1654 }
1655 })
1656 }
1657 }
1658
1659 func TestFillFloat(t *testing.T) {
1660
1661
1662 want := `{
1663 x: 3.14
1664 }`
1665
1666 filltest := func(x interface{}) {
1667 r := &Runtime{}
1668 i, err := r.Compile("test", `
1669 x: number
1670 `)
1671 if err != nil {
1672 t.Fatal(err)
1673 }
1674
1675 i, err = i.Fill(x, "x")
1676 if err != nil {
1677 t.Fatal(err)
1678 }
1679
1680 got := fmt.Sprint(i.Value())
1681 if got != want {
1682 t.Errorf("got: %s\nwant: %s", got, want)
1683 }
1684 }
1685
1686 filltest(float32(3.14))
1687 filltest(float64(3.14))
1688 filltest(big.NewFloat(3.14))
1689 }
1690
1691 func TestValue_LookupDef(t *testing.T) {
1692 r := &Runtime{}
1693
1694 testCases := []struct {
1695 in string
1696 def string
1697 exists bool
1698 out string
1699 }{{
1700 in: `#foo: 3`,
1701 def: "#foo",
1702 out: `3`,
1703 }, {
1704 in: `_foo: 3`,
1705 def: "_foo",
1706 out: `_|_ // field not found: #_foo`,
1707 }, {
1708 in: `_#foo: 3`,
1709 def: "_#foo",
1710 out: `_|_ // field not found: _#foo`,
1711 }, {
1712 in: `"foo", #foo: 3`,
1713 def: "#foo",
1714 out: `3`,
1715 }}
1716
1717 for _, tc := range testCases {
1718 t.Run(tc.def, func(t *testing.T) {
1719 v := compileT(t, r, tc.in).Value()
1720 v = v.LookupDef(tc.def)
1721 got := fmt.Sprint(v)
1722
1723 if got != tc.out {
1724 t.Errorf("\ngot: %s\nwant: %s", got, tc.out)
1725 }
1726 })
1727 }
1728 }
1729
1730
1731 func TestDefaults(t *testing.T) {
1732 testCases := []struct {
1733 value string
1734 def string
1735 val string
1736 ok bool
1737 }{{
1738 value: `number | *1`,
1739 def: "1",
1740 val: "number",
1741 ok: true,
1742 }, {
1743 value: `1 | 2 | *3`,
1744 def: "3",
1745 val: "1|2|3",
1746 ok: true,
1747 }, {
1748 value: `*{a:1,b:2}|{a:1}|{b:2}`,
1749 def: "{a:1,b:2}",
1750 val: "{a: 1}|{b: 2}",
1751 ok: true,
1752 }, {
1753 value: `{a:1}&{b:2}`,
1754 def: `{a:1,b:2}`,
1755 val: ``,
1756 ok: false,
1757 }, {
1758 value: `*_|_ | (*"x" | string)`,
1759 def: `"x" | string`,
1760 val: `"x"|string`,
1761 ok: false,
1762 }}
1763 for _, tc := range testCases {
1764 t.Run(tc.value, func(t *testing.T) {
1765 v := getInstance(t, "a: "+tc.value).Lookup("a")
1766
1767 v = v.Eval()
1768 d, ok := v.Default()
1769 if ok != tc.ok {
1770 t.Errorf("hasDefault: got %v; want %v", ok, tc.ok)
1771 }
1772
1773 if got := compactRawStr(d); got != tc.def {
1774 t.Errorf("default: got %v; want %v", got, tc.def)
1775 }
1776
1777 op, val := d.Expr()
1778 if op != OrOp {
1779 return
1780 }
1781 vars := []string{}
1782 for _, v := range val {
1783 vars = append(vars, fmt.Sprint(v))
1784 }
1785 if got := strings.Join(vars, "|"); got != tc.val {
1786 t.Errorf("value: got %v; want %v", got, tc.val)
1787 }
1788 })
1789 }
1790 }
1791
1792 func TestLen(t *testing.T) {
1793 testCases := []struct {
1794 input string
1795 length string
1796 }{{
1797 input: "[1, 3]",
1798 length: "2",
1799 }, {
1800 input: "[1, 3, ...]",
1801 length: "int & >=2",
1802 }, {
1803 input: `"foo"`,
1804 length: "3",
1805 }, {
1806 input: `'foo'`,
1807 length: "3",
1808
1809
1810
1811
1812 }, {
1813 input: "3",
1814 length: "_|_ // len not supported for type int",
1815 }}
1816 for _, tc := range testCases {
1817 t.Run(tc.input, func(t *testing.T) {
1818 v := getInstance(t, "a: "+tc.input).Lookup("a")
1819
1820 length := v.Len()
1821 if got := fmt.Sprint(length); got != tc.length {
1822 t.Errorf("length: got %v; want %v", got, tc.length)
1823 }
1824 })
1825 }
1826 }
1827
1828 func TestTemplate(t *testing.T) {
1829 testCases := []struct {
1830 value string
1831 path []string
1832 want string
1833 }{{
1834 value: `
1835 a: [Name=string]: Name
1836 `,
1837 path: []string{"a", ""},
1838 want: `"label"`,
1839 }, {
1840 value: `
1841 [Name=string]: { a: Name }
1842 `,
1843 path: []string{"", "a"},
1844 want: `"label"`,
1845 }, {
1846 value: `
1847 [Name=string]: { a: Name }
1848 `,
1849 path: []string{""},
1850 want: `{"a":"label"}`,
1851 }, {
1852 value: `
1853 a: [Foo=string]: [Bar=string]: { b: Foo+Bar }
1854 `,
1855 path: []string{"a", "", ""},
1856 want: `{"b":"labellabel"}`,
1857 }, {
1858 value: `
1859 a: [Foo=string]: b: [Bar=string]: { c: Foo+Bar }
1860 a: foo: b: [Bar=string]: { d: Bar }
1861 `,
1862 path: []string{"a", "foo", "b", ""},
1863 want: `{"c":"foolabel","d":"label"}`,
1864 }}
1865 for _, tc := range testCases {
1866 t.Run("", func(t *testing.T) {
1867 v := getInstance(t, tc.value).Value()
1868 for _, p := range tc.path {
1869 if p == "" {
1870 v = v.Template()("label")
1871 } else {
1872 v = v.Lookup(p)
1873 }
1874 }
1875 b, err := v.MarshalJSON()
1876 if err != nil {
1877 t.Fatal(err)
1878 }
1879 if got := string(b); got != tc.want {
1880 t.Errorf("\n got: %q\nwant: %q", got, tc.want)
1881 }
1882 })
1883 }
1884 }
1885
1886 func TestElem(t *testing.T) {
1887 testCases := []struct {
1888 value string
1889 path []string
1890 want string
1891 }{{
1892 value: `
1893 a: [...int]
1894 `,
1895 path: []string{"a", ""},
1896 want: `int`,
1897 }, {
1898 value: `
1899 [Name=string]: { a: Name }
1900 `,
1901 path: []string{"", "a"},
1902 want: `string`,
1903 }, {
1904 value: `
1905 [Name=string]: { a: Name }
1906 `,
1907 path: []string{""},
1908 want: "{\n\ta: string\n}",
1909 }, {
1910 value: `
1911 a: [Foo=string]: [Bar=string]: { b: Foo+Bar }
1912 `,
1913 path: []string{"a", "", ""},
1914 want: "{\n\tb: string + string\n}",
1915 }, {
1916 value: `
1917 a: [Foo=string]: b: [Bar=string]: { c: Foo+Bar }
1918 a: foo: b: [Bar=string]: { d: Bar }
1919 `,
1920 path: []string{"a", "foo", "b", ""},
1921 want: "{\n\tc: \"foo\" + string\n\td: string\n}",
1922 }}
1923 for _, tc := range testCases {
1924 t.Run("", func(t *testing.T) {
1925 v := getInstance(t, tc.value).Value()
1926 v.v.Finalize(v.ctx())
1927 for _, p := range tc.path {
1928 if p == "" {
1929 var ok bool
1930 v, ok = v.Elem()
1931 if !ok {
1932 t.Fatal("expected element")
1933 }
1934 } else {
1935 v = v.Lookup(p)
1936 }
1937 }
1938 got := fmt.Sprint(v)
1939
1940 if got != tc.want {
1941 t.Errorf("\n got: %q\nwant: %q", got, tc.want)
1942 }
1943 })
1944 }
1945 }
1946
1947 func TestSubsume(t *testing.T) {
1948 a := ParsePath("a")
1949 b := ParsePath("b")
1950 testCases := []struct {
1951 value string
1952 pathA Path
1953 pathB Path
1954 options []Option
1955 want bool
1956 }{{
1957 value: `4`,
1958 want: true,
1959 }, {
1960 value: `a: string, b: "foo"`,
1961 pathA: a,
1962 pathB: b,
1963 want: true,
1964 }, {
1965 value: `a: string, b: "foo"`,
1966 pathA: b,
1967 pathB: a,
1968 want: false,
1969 }, {
1970 value: `a: {a: string, b: 4}, b: {a: "foo", b: 4}`,
1971 pathA: a,
1972 pathB: b,
1973 want: true,
1974 }, {
1975 value: `a: [string, 4], b: ["foo", 4]`,
1976 pathA: a,
1977 pathB: b,
1978 want: true,
1979 }, {
1980 value: `a: [...string], b: ["foo"]`,
1981 pathA: a,
1982 pathB: b,
1983 want: true,
1984 }, {
1985 value: `a: [...int], b: ["foo"]`,
1986 pathA: a,
1987 pathB: b,
1988 want: false,
1989 }, {
1990
1991
1992 value: `
1993 #Run: { action: "run", command: [...string] }
1994 b: { action: "run", command: ["echo", "hello"] }
1995 `,
1996 pathA: ParsePath("#Run"),
1997 pathB: b,
1998
1999
2000
2001
2002
2003 want: true,
2004 }, {
2005
2006
2007 value: `
2008 #Run: { action: "run", command: [...string] }
2009 b: { action: "run", command: ["echo", "hello"] }
2010 `,
2011 pathA: ParsePath("#Run"),
2012 pathB: b,
2013 options: []Option{Final()},
2014 want: true,
2015 }, {
2016
2017 value: `
2018 a: <5
2019 b: *3 | int
2020 `,
2021 pathA: a,
2022 pathB: b,
2023 want: true,
2024 }, {
2025
2026 value: `
2027 a: <5
2028 b: *3 | int
2029 `,
2030 pathA: a,
2031 pathB: b,
2032 options: []Option{Raw()},
2033 want: false,
2034 }, {
2035 value: `
2036 #A: {
2037 exact: string
2038 } | {
2039 regex: string
2040 }
2041 #B: {
2042 exact: string
2043 } | {
2044 regex: string
2045 }
2046 `,
2047 pathA: ParsePath("#A"),
2048 pathB: ParsePath("#B"),
2049 options: []Option{},
2050 want: true,
2051 }}
2052 for _, tc := range testCases {
2053 t.Run(tc.value, func(t *testing.T) {
2054 v := getInstance(t, tc.value)
2055 a := v.Value().LookupPath(tc.pathA)
2056 b := v.Value().LookupPath(tc.pathB)
2057 got := a.Subsume(b, tc.options...) == nil
2058 if got != tc.want {
2059 t.Errorf("got %v (%v); want %v (%v)", got, a, tc.want, b)
2060 }
2061 })
2062 }
2063 }
2064
2065 func TestSubsumes(t *testing.T) {
2066 a := []string{"a"}
2067 b := []string{"b"}
2068 testCases := []struct {
2069 value string
2070 pathA []string
2071 pathB []string
2072 want bool
2073 }{{
2074 value: `4`,
2075 want: true,
2076 }, {
2077 value: `a: string, b: "foo"`,
2078 pathA: a,
2079 pathB: b,
2080 want: true,
2081 }, {
2082 value: `a: string, b: "foo"`,
2083 pathA: b,
2084 pathB: a,
2085 want: false,
2086 }, {
2087 value: `a: {a: string, b: 4}, b: {a: "foo", b: 4}`,
2088 pathA: a,
2089 pathB: b,
2090 want: true,
2091 }, {
2092 value: `a: [string, 4], b: ["foo", 4]`,
2093 pathA: a,
2094 pathB: b,
2095 want: true,
2096 }, {
2097 value: `a: [...string], b: ["foo"]`,
2098 pathA: a,
2099 pathB: b,
2100 want: true,
2101 }, {
2102 value: `a: [...int], b: ["foo"]`,
2103 pathA: a,
2104 pathB: b,
2105 want: false,
2106 }, {
2107 value: `
2108 a: { action: "run", command: [...string] }
2109 b: { action: "run", command: ["echo", "hello"] }
2110 `,
2111 pathA: a,
2112 pathB: b,
2113 want: true,
2114 }}
2115 for _, tc := range testCases {
2116 t.Run(tc.value, func(t *testing.T) {
2117 v := getInstance(t, tc.value)
2118 a := v.Lookup(tc.pathA...)
2119 b := v.Lookup(tc.pathB...)
2120 got := a.Subsumes(b)
2121 if got != tc.want {
2122 t.Errorf("got %v (%v); want %v (%v)", got, a, tc.want, b)
2123 }
2124 })
2125 }
2126 }
2127
2128 func TestUnify(t *testing.T) {
2129 a := "a"
2130 b := "b"
2131 type testCase struct {
2132 value string
2133 pathA string
2134 pathB string
2135 want string
2136 }
2137 testCases := []testCase{{
2138 value: `4`,
2139 want: `4`,
2140 }, {
2141 value: `a: string, b: "foo"`,
2142 pathA: a,
2143 pathB: b,
2144 want: `"foo"`,
2145 }, {
2146 value: `a: string, b: "foo"`,
2147 pathA: b,
2148 pathB: a,
2149 want: `"foo"`,
2150 }, {
2151 value: `a: {a: string, b: 4}, b: {a: "foo", b: 4}`,
2152 pathA: a,
2153 pathB: b,
2154 want: `{"a":"foo","b":4}`,
2155 }, {
2156 value: `a: [string, 4], b: ["foo", 4]`,
2157 pathA: a,
2158 pathB: b,
2159 want: `["foo",4]`,
2160 }, {
2161 value: `a: {a: string, _hidden: int, _#hidden: int}, b: close({a: "foo"})`,
2162 pathA: a,
2163 pathB: b,
2164 want: `{"a":"foo"}`,
2165 }, {
2166
2167 value: `#T: {
2168 ...
2169 }
2170 b: {
2171 let foobar = {}
2172 _fb: foobar
2173 }`,
2174 pathA: "#T",
2175 pathB: b,
2176 want: `{}`,
2177 }, {
2178 value: `
2179 a: #A: "foo"
2180 #B: {...}
2181 `,
2182 pathA: a,
2183 pathB: "#B",
2184 want: `{}`,
2185 }}
2186
2187 tdtest.Run(t, testCases, func(t *cuetest.T, tc *testCase) {
2188 v := getInstance(t.T, tc.value).Value()
2189 x := v.LookupPath(ParsePath(tc.pathA))
2190 y := v.LookupPath(ParsePath(tc.pathB))
2191 b, err := x.Unify(y).MarshalJSON()
2192 if err != nil {
2193 t.Fatal(err)
2194 }
2195 t.Equal(string(b), tc.want)
2196 })
2197 }
2198
2199 func TestUnifyAccept(t *testing.T) {
2200 type testCase struct {
2201 value string
2202 want string
2203 }
2204 testCases := []testCase{{
2205 value: `#v: 4, #w: 4, #accept: int`,
2206 want: `4`,
2207 }, {
2208 value: `#v: string, #w: "foo", #accept: string`,
2209 want: `"foo"`,
2210 }, {
2211 value: `#v: {a: "foo"}, #w: {b: 4}, #accept: {a: string, b: int}`,
2212 want: `{"a":"foo","b":4}`,
2213 }, {
2214 value: `#v: [string, 4], #w: ["foo", 4], #accept: [string, int, ...]`,
2215 want: `["foo",4]`,
2216 }, {
2217 value: `#v: {a: string, b: 1, _#hidden: int}, #w: {a: "foo"}, #accept: {...}`,
2218 want: `{"a":"foo","b":1}`,
2219 }, {
2220
2221 value: `#accept: {
2222 ...
2223 }
2224 #v: {}
2225 #w: {
2226 let foobar = {}
2227 _fb: foobar
2228 }`,
2229 want: `{}`,
2230 }, {
2231 value: `
2232 #v: #v: "foo"
2233 #w: {b:1}
2234 #accept: {...}
2235 `,
2236 want: `{"b":1}`,
2237 }}
2238
2239 tdtest.Run(t, testCases, func(t *cuetest.T, tc *testCase) {
2240 v := getInstance(t.T, tc.value).Value()
2241 x := v.LookupPath(ParsePath("#v"))
2242 y := v.LookupPath(ParsePath("#w"))
2243 a := v.LookupPath(ParsePath("#accept"))
2244 b, err := x.UnifyAccept(y, a).MarshalJSON()
2245 if err != nil {
2246 t.Fatal(err)
2247 }
2248 t.Equal(string(b), tc.want)
2249 })
2250 }
2251
2252 func TestEquals(t *testing.T) {
2253 testCases := []struct {
2254 a, b string
2255 want bool
2256 }{{
2257 `4`, `4`, true,
2258 }, {
2259 `"str"`, `2`, false,
2260 }, {
2261 `2`, `3`, false,
2262 }, {
2263 `[1]`, `[3]`, false,
2264 }, {
2265 `[{a: 1,...}]`, `[{a: 1,...}]`, true,
2266 }, {
2267 `[]`, `[]`, true,
2268 }, {
2269 `{
2270 a: b,
2271 b: a,
2272 }`,
2273 `{
2274 a: b,
2275 b: a,
2276 }`,
2277 true,
2278 }, {
2279 `{
2280 a: "foo",
2281 b: "bar",
2282 }`,
2283 `{
2284 a: "foo",
2285 }`,
2286 false,
2287 }, {
2288
2289 `{ #Foo: { k: 1 }, a: #Foo }`,
2290 `{ #Foo: { k: 1 }, a: { k: 1 } }`,
2291 true,
2292 }, {
2293
2294 `{ #Foo: { k: 1 }, a: #Foo }`,
2295 `{ #Foo: { k: 1 }, a: { #Foo, i?: 1 } }`,
2296 true,
2297 }, {
2298
2299 `{ a: 2, b: { 3 } }`,
2300 `{ a: { 2 }, b: 3 }`,
2301 true,
2302 }}
2303 for _, tc := range testCases {
2304 t.Run("", func(t *testing.T) {
2305 var r Runtime
2306 a, err := r.Compile("a", tc.a)
2307 if err != nil {
2308 t.Fatal(err)
2309 }
2310 b, err := r.Compile("b", tc.b)
2311 if err != nil {
2312 t.Fatal(err)
2313 }
2314 got := a.Value().Equals(b.Value())
2315 if got != tc.want {
2316 t.Errorf("got %v; want %v", got, tc.want)
2317 }
2318 })
2319 }
2320 }
2321
2322
2323 func TestValidate(t *testing.T) {
2324 testCases := []struct {
2325 desc string
2326 in string
2327 err bool
2328 opts []Option
2329 }{{
2330 desc: "issue #51",
2331 in: `
2332 a: [string]: foo
2333 a: b: {}
2334 `,
2335 err: true,
2336 }, {
2337 desc: "concrete",
2338 in: `
2339 a: 1
2340 b: { c: 2, d: 3 }
2341 c: d: e: f: 5
2342 g?: int
2343 `,
2344 opts: []Option{Concrete(true)},
2345 }, {
2346 desc: "definition error",
2347 in: `
2348 #b: 1 & 2
2349 `,
2350 opts: []Option{},
2351 err: true,
2352 }, {
2353 desc: "definition error okay if optional",
2354 in: `
2355 #b?: 1 & 2
2356 `,
2357 opts: []Option{},
2358 }, {
2359 desc: "definition with optional",
2360 in: `
2361 #b: {
2362 a: int
2363 b?: >=0
2364 }
2365 `,
2366 opts: []Option{Concrete(true)},
2367 }, {
2368 desc: "disjunction",
2369 in: `a: 1 | 2`,
2370 }, {
2371 desc: "disjunction concrete",
2372 in: `a: 1 | 2`,
2373 opts: []Option{Concrete(true)},
2374 err: true,
2375 }, {
2376 desc: "incomplete concrete",
2377 in: `a: string`,
2378 }, {
2379 desc: "incomplete",
2380 in: `a: string`,
2381 opts: []Option{Concrete(true)},
2382 err: true,
2383 }, {
2384 desc: "list",
2385 in: `a: [{b: string}, 3]`,
2386 }, {
2387 desc: "list concrete",
2388 in: `a: [{b: string}, 3]`,
2389 opts: []Option{Concrete(true)},
2390 err: true,
2391 }, {
2392 desc: "allow cycles",
2393 in: `
2394 a: b - 100
2395 b: a + 100
2396 c: [c[1], c[0]]
2397 `,
2398 }, {
2399 desc: "disallow cycles",
2400 in: `
2401 a: b - 100
2402 b: a + 100
2403 c: [c[1], c[0]]
2404 `,
2405 opts: []Option{DisallowCycles(true)},
2406 err: true,
2407 }, {
2408 desc: "builtins are okay",
2409 in: `
2410 import "time"
2411
2412 a: { b: time.Duration } | { c: time.Duration }
2413 `,
2414 }, {
2415 desc: "comprehension error",
2416 in: `
2417 a: { if b == "foo" { field: 2 } }
2418 `,
2419 err: true,
2420 }, {
2421 desc: "ignore optional in schema",
2422 in: `
2423 #Schema1: {
2424 a?: int
2425 }
2426 instance1: #Schema1
2427 `,
2428 opts: []Option{Concrete(true)},
2429 }, {
2430 desc: "issue324",
2431 in: `
2432 import "encoding/yaml"
2433
2434 x: string
2435 a: b: c: *["\(x)"] | _
2436 d: yaml.Marshal(a.b)
2437 `,
2438 }, {
2439 desc: "allow non-concrete values for definitions",
2440 in: `
2441 variables: #variables
2442
2443 {[!~"^[.]"]: #job}
2444
2445 #variables: [string]: int | string
2446
2447 #job: ({a: int} | {b: int}) & {
2448 "variables"?: #variables
2449 }
2450 `,
2451 }}
2452 for _, tc := range testCases {
2453 t.Run(tc.desc, func(t *testing.T) {
2454 r := Runtime{}
2455 inst, err := r.Parse("validate", tc.in)
2456 if err == nil {
2457 err = inst.Value().Validate(tc.opts...)
2458 }
2459 if gotErr := err != nil; gotErr != tc.err {
2460 t.Errorf("got %v; want %v", err, tc.err)
2461 }
2462 })
2463 }
2464 }
2465
2466 func TestPath(t *testing.T) {
2467 config := `
2468 a: b: c: 5
2469 b: {
2470 b1: 3
2471 b2: 4
2472 "b 3": 5
2473 "4b": 6
2474 l: [
2475 {a: 2},
2476 {c: 2},
2477 ]
2478 }
2479 `
2480 mkpath := func(p ...string) []string { return p }
2481 testCases := [][]string{
2482 mkpath("a", "b", "c"),
2483 mkpath("b", "l", "1", "c"),
2484 mkpath("b", `"b 3"`),
2485 mkpath("b", `"4b"`),
2486 }
2487 for _, tc := range testCases {
2488 r := Runtime{}
2489 inst, err := r.Parse("config", config)
2490 if err != nil {
2491 t.Fatal(err)
2492 }
2493 t.Run(strings.Join(tc, "."), func(t *testing.T) {
2494 v := inst.Lookup(tc[0])
2495 for _, e := range tc[1:] {
2496 if '0' <= e[0] && e[0] <= '9' {
2497 i, err := strconv.Atoi(e)
2498 if err != nil {
2499 t.Fatal(err)
2500 }
2501 iter, err := v.List()
2502 if err != nil {
2503 t.Fatal(err)
2504 }
2505 for c := 0; iter.Next(); c++ {
2506 if c == i {
2507 v = iter.Value()
2508 break
2509 }
2510 }
2511 } else if e[0] == '"' {
2512 v = v.Lookup(e[1 : len(e)-1])
2513 } else {
2514 v = v.Lookup(e)
2515 }
2516 }
2517 got := pathToStrings(v.Path())
2518 if !reflect.DeepEqual(got, tc) {
2519 t.Errorf("got %v; want %v", got, tc)
2520 }
2521 })
2522 }
2523 }
2524
2525 func TestValueLookup(t *testing.T) {
2526 config := `
2527 a: {
2528 a: 0
2529 b: 1
2530 c: 2
2531 }
2532 b: {
2533 d: a.a
2534 e: int
2535 }
2536 `
2537
2538 strList := func(s ...string) []string { return s }
2539
2540 testCases := []struct {
2541 config string
2542 path []string
2543 str string
2544 notExists bool
2545 }{{
2546 config: "_|_",
2547 path: strList(""),
2548 str: "explicit error (_|_ literal) in source",
2549 }, {
2550 config: "_|_",
2551 path: strList("a"),
2552 str: "explicit error (_|_ literal) in source",
2553 }, {
2554 config: config,
2555 path: strList(),
2556 str: "{a:{a:0,b:1,c:2},b:{d:0,e:int}",
2557 }, {
2558 config: config,
2559 path: strList("a", "a"),
2560 str: "0",
2561 }, {
2562 config: config,
2563 path: strList("a"),
2564 str: "{a:0,b:1,c:2}",
2565 }, {
2566 config: config,
2567 path: strList("b", "d"),
2568 str: "0",
2569 }, {
2570 config: config,
2571 path: strList("c", "non-existing"),
2572 str: "not found",
2573 notExists: true,
2574 }, {
2575 config: config,
2576 path: strList("b", "d", "lookup in non-struct"),
2577 str: "cannot use value 0 (type int) as struct",
2578 }}
2579 for _, tc := range testCases {
2580 t.Run(tc.str, func(t *testing.T) {
2581 v := getInstance(t, tc.config).Value().Lookup(tc.path...)
2582 if got := !v.Exists(); got != tc.notExists {
2583 t.Errorf("exists: got %v; want %v", got, tc.notExists)
2584 }
2585
2586 got := v.ctx().Str(v.v)
2587 if tc.str == "" {
2588 t.Fatalf("str empty, got %q", got)
2589 }
2590 if !strings.Contains(got, tc.str) {
2591 t.Errorf("\n got %v\nwant %v", got, tc.str)
2592 }
2593 })
2594 }
2595 }
2596
2597 func cmpError(a, b error) bool {
2598 if a == nil {
2599 return b == nil
2600 }
2601 if b == nil {
2602 return a == nil
2603 }
2604 return a.Error() == b.Error()
2605 }
2606
2607
2608 func TestValueDoc(t *testing.T) {
2609 const config = `
2610 // foobar defines at least foo.
2611 package foobar
2612
2613 // A Foo fooses stuff.
2614 Foo: {
2615 // field1 is an int.
2616 field1: int
2617
2618 field2: int
2619
2620 // duplicate field comment
2621 dup3: int
2622 }
2623
2624 // foos are instances of Foo.
2625 foos: [string]: Foo
2626
2627 // My first little foo.
2628 foos: MyFoo: {
2629 // local field comment.
2630 field1: 0
2631
2632 // Dangling comment.
2633
2634 // other field comment.
2635 field2: 1
2636
2637 // duplicate field comment
2638 dup3: int
2639 }
2640
2641 bar: {
2642 // comment from bar on field 1
2643 field1: int
2644 // comment from bar on field 2
2645 field2: int // don't include this
2646 }
2647
2648 baz: bar & {
2649 // comment from baz on field 1
2650 field1: int
2651 field2: int
2652 }
2653 `
2654 config2 := `
2655 // Another Foo.
2656 Foo: {}
2657 `
2658 var r Runtime
2659 getInst := func(name, body string) *Instance {
2660 inst, err := r.Compile("dir/file1.cue", body)
2661 if err != nil {
2662 t.Fatal(err)
2663 }
2664 return inst
2665 }
2666
2667 inst := getInst("config", config)
2668
2669 v1 := inst.Value()
2670 v2 := getInst("config2", config2).Value()
2671 both := v1.Unify(v2)
2672
2673 testCases := []struct {
2674 val Value
2675 path string
2676 doc string
2677 }{{
2678 val: v1,
2679 path: "foos",
2680 doc: "foos are instances of Foo.\n",
2681 }, {
2682 val: v1,
2683 path: "foos MyFoo",
2684 doc: "My first little foo.\n",
2685 }, {
2686 val: v1,
2687 path: "foos MyFoo field1",
2688 doc: `local field comment.
2689
2690 field1 is an int.
2691 `,
2692 }, {
2693 val: v1,
2694 path: "foos MyFoo field2",
2695 doc: "other field comment.\n",
2696 }, {
2697
2698 val: v1,
2699 path: "foos MyFoo dup3",
2700 doc: "duplicate field comment\n",
2701 }, {
2702 val: v1,
2703 path: "bar field1",
2704 doc: "comment from bar on field 1\n",
2705 }, {
2706 val: v1,
2707 path: "baz field1",
2708 doc: `comment from bar on field 1
2709
2710 comment from baz on field 1
2711 `,
2712 }, {
2713 val: v1,
2714 path: "baz field2",
2715 doc: "comment from bar on field 2\n",
2716 }, {
2717 val: v2,
2718 path: "Foo",
2719 doc: `Another Foo.
2720 `,
2721 }, {
2722 val: both,
2723 path: "Foo",
2724 doc: `A Foo fooses stuff.
2725
2726 Another Foo.
2727 `,
2728 }}
2729 for _, tc := range testCases {
2730 t.Run("field:"+tc.path, func(t *testing.T) {
2731 v := tc.val.Lookup(strings.Split(tc.path, " ")...)
2732 doc := docStr(v.Doc())
2733 if doc != tc.doc {
2734 t.Errorf("doc: got:\n%vwant:\n%v", doc, tc.doc)
2735 }
2736 })
2737 }
2738 want := "foobar defines at least foo.\n"
2739 if got := docStr(inst.Value().Doc()); got != want {
2740 t.Errorf("pkg: got:\n%vwant:\n%v", got, want)
2741 }
2742 }
2743
2744 func docStr(docs []*ast.CommentGroup) string {
2745 doc := ""
2746 for _, d := range docs {
2747 if doc != "" {
2748 doc += "\n"
2749 }
2750 doc += d.Text()
2751 }
2752 return doc
2753 }
2754
2755
2756
2757 func TestMarshalJSON(t *testing.T) {
2758 type testCase struct {
2759 value string
2760 json string
2761 err string
2762 }
2763 testCases := []testCase{{
2764 value: `""`,
2765 json: `""`,
2766 }, {
2767 value: `null`,
2768 json: `null`,
2769 }, {
2770 value: `_|_`,
2771 err: "explicit error (_|_ literal) in source",
2772 }, {
2773 value: `(a.b)
2774 a: {}`,
2775 err: "undefined field",
2776 }, {
2777 value: `true`,
2778 json: `true`,
2779 }, {
2780 value: `false`,
2781 json: `false`,
2782 }, {
2783 value: `bool`,
2784 err: "cannot convert incomplete value",
2785 }, {
2786 value: `"str"`,
2787 json: `"str"`,
2788 }, {
2789 value: `12_000`,
2790 json: `12000`,
2791 }, {
2792 value: `12.000`,
2793 json: `12.000`,
2794 }, {
2795 value: `12M`,
2796 json: `12000000`,
2797 }, {
2798 value: `3.0e100`,
2799 json: `3.0E+100`,
2800 }, {
2801 value: `0/0`,
2802 err: "division undefined",
2803 }, {
2804 value: `[]`,
2805 json: `[]`,
2806 }, {
2807 value: `[1, 2, 3]`,
2808 json: `[1,2,3]`,
2809 }, {
2810 value: `[int]`,
2811 err: `0: cannot convert incomplete value`,
2812 }, {
2813 value: `{}`,
2814 json: `{}`,
2815 }, {
2816 value: `{a: 2, b: 3, c: ["A", "B"]}`,
2817 json: `{"a":2,"b":3,"c":["A","B"]}`,
2818 }, {
2819 value: `{a: 2, b: 3, c: [string, "B"]}`,
2820 err: `c.0: cannot convert incomplete value`,
2821 }, {
2822 value: `{a: [{b: [0, {c: string}] }] }`,
2823 err: `a.0.b.1.c: cannot convert incomplete value`,
2824 }, {
2825 value: `{foo?: 1, bar?: 2, baz: 3}`,
2826 json: `{"baz":3}`,
2827 }, {
2828 value: `{foo!: 1, bar: 2}`,
2829 err: "cue: marshal error: foo: field is required but not present",
2830 }, {
2831
2832
2833 value: `{foo?: bar, bar?: foo, baz: 3}`,
2834 json: `{"baz":3}`,
2835 }, {
2836
2837 value: `a: 1.0/1`,
2838 json: `{"a":1.0}`,
2839 }, {
2840
2841 value: `
2842 a: int
2843 a: >0
2844 a: <2
2845
2846 b: int
2847 b: >=0.9
2848 b: <1.1
2849
2850 c: int
2851 c: >1
2852 c: <=2
2853
2854 d: int
2855 d: >=1
2856 d: <=1.5
2857
2858 e: int
2859 e: >=1
2860 e: <=1.32
2861
2862 f: >=1.1 & <=1.1
2863 `,
2864 json: `{"a":1,"b":1,"c":2,"d":1,"e":1,"f":1.1}`,
2865 }, {
2866 value: `
2867 #Task: {
2868 {
2869 op: "pull"
2870 tag: *"latest" | string
2871 tagInString: tag + "dd"
2872 } | {
2873 op: "scratch"
2874 }
2875 }
2876
2877 foo: #Task & {"op": "pull"}
2878 `,
2879 json: `{"foo":{"op":"pull","tag":"latest","tagInString":"latestdd"}}`,
2880 }, {
2881
2882 value: `x: "\(string)": "v"`,
2883 err: `x: invalid interpolation`,
2884 }, {
2885
2886 value: `x: "\(bool)": "v"`,
2887 err: `invalid interpolation`,
2888 }, {
2889
2890 value: `
2891 x: {
2892 for k, v in y {
2893 "\(k)": v
2894 }
2895 }
2896 y: {}
2897 `,
2898 json: `{"x":{},"y":{}}`,
2899 }, {
2900
2901 value: `
2902 x: {
2903 for k, v in y {
2904 "\(k)": v
2905 }
2906 }
2907 y: _
2908 `,
2909 err: `x: cannot range over y (incomplete type _)`,
2910 }, {
2911 value: `
2912 package foo
2913
2914 #SomeBaseType: {
2915 "a" | "b"
2916 #AUTO: "z"
2917 }
2918
2919 V1: ("x" | "y") | *"z"
2920 V2: ("x" | "y") | *#SomeBaseType.#AUTO
2921 `,
2922 err: "cue: marshal error: V2: cannot convert incomplete value \"|((string){ \\\"x\\\" }, (string){ \\\"y\\\" })\" to JSON",
2923 }}
2924 for i, tc := range testCases {
2925 t.Run(fmt.Sprintf("%d/%v", i, tc.value), func(t *testing.T) {
2926 inst := getInstance(t, tc.value)
2927 b, err := inst.Value().MarshalJSON()
2928 checkFatal(t, err, tc.err, "init")
2929
2930 if got := string(b); got != tc.json {
2931 t.Errorf("\n got %v;\nwant %v", got, tc.json)
2932 }
2933 })
2934 }
2935 }
2936
2937 func TestWalk(t *testing.T) {
2938 testCases := []struct {
2939 value string
2940 out string
2941 }{{
2942 value: `""`,
2943 out: `""`,
2944 }, {
2945 value: `null`,
2946 out: `null`,
2947 }, {
2948 value: `_|_`,
2949 out: "_|_(explicit error (_|_ literal) in source)",
2950 }, {
2951 value: `(a.b)
2952 a: {}`,
2953 out: `_|_(undefined field: b)`,
2954 }, {
2955 value: `true`,
2956 out: `true`,
2957 }, {
2958 value: `false`,
2959 out: `false`,
2960 }, {
2961 value: `bool`,
2962 out: "bool",
2963 }, {
2964 value: `"str"`,
2965 out: `"str"`,
2966 }, {
2967 value: `12_000`,
2968 out: `12000`,
2969
2970 }, {
2971 value: `12.000`,
2972 out: `12.000`,
2973 }, {
2974 value: `12M`,
2975 out: `12000000`,
2976
2977 }, {
2978 value: `3.0e100`,
2979 out: `3.0e+100`,
2980
2981 }, {
2982 value: `[]`,
2983 out: `[]`,
2984 }, {
2985 value: `[1, 2, 3]`,
2986 out: `[1,2,3]`,
2987 }, {
2988 value: `[int]`,
2989 out: `[int]`,
2990 }, {
2991 value: `3 * [1, 2]`,
2992 out: `[1,2,1,2,1,2]`,
2993 }, {
2994 value: `{}`,
2995 out: `{}`,
2996 }, {
2997 value: `{a: 2, b: 3, c: ["A", "B"]}`,
2998 out: `{a:2,b:3,c:["A","B"]}`,
2999 }}
3000 for i, tc := range testCases {
3001 t.Run(fmt.Sprintf("%d/%v", i, tc.value), func(t *testing.T) {
3002 inst := getInstance(t, tc.value)
3003 buf := []byte{}
3004 stripComma := func() {
3005 if n := len(buf) - 1; buf[n] == ',' {
3006 buf = buf[:n]
3007 }
3008 }
3009 inst.Value().Walk(func(v Value) bool {
3010 v = v.Eval()
3011 if !v.v.Label.IsInt() {
3012 if k, ok := v.Label(); ok {
3013 buf = append(buf, k+":"...)
3014 }
3015 }
3016 switch v.Kind() {
3017 case StructKind:
3018 buf = append(buf, '{')
3019 case ListKind:
3020 buf = append(buf, '[')
3021 default:
3022 if b, _ := v.v.BaseValue.(*adt.Bottom); b != nil {
3023 s := debugStr(v.ctx(), b)
3024 buf = append(buf, fmt.Sprint(s, ",")...)
3025 return true
3026 }
3027 buf = append(buf, fmt.Sprint(v, ",")...)
3028 }
3029 return true
3030 }, func(v Value) {
3031 switch v.Kind() {
3032 case StructKind:
3033 stripComma()
3034 buf = append(buf, "},"...)
3035 case ListKind:
3036 stripComma()
3037 buf = append(buf, "],"...)
3038 }
3039 })
3040 stripComma()
3041 if got := string(buf); got != tc.out {
3042 t.Errorf("\n got %v;\nwant %v", got, tc.out)
3043 }
3044 })
3045 }
3046 }
3047
3048 func TestTrimZeros(t *testing.T) {
3049 testCases := []struct {
3050 in string
3051 out string
3052 }{
3053 {"", ""},
3054 {"2", "2"},
3055 {"2.0", "2.0"},
3056 {"2.000000000000", "2.0"},
3057 {"2000000000000", "2e+12"},
3058 {"2000000", "2e+6"},
3059 }
3060 for _, tc := range testCases {
3061 t.Run(tc.in, func(t *testing.T) {
3062 if got := trimZeros(tc.in); got != tc.out {
3063 t.Errorf("got %q; want %q", got, tc.out)
3064 }
3065 })
3066 }
3067 }
3068
3069 func TestReferencePath(t *testing.T) {
3070 testCases := []struct {
3071 input string
3072 want string
3073 wantImportPath string
3074 alt string
3075 }{{
3076 input: "v: w: x: _|_",
3077 want: "",
3078 }, {
3079 input: "v: w: x: 2",
3080 want: "",
3081 }, {
3082 input: "v: w: x: a, a: 1",
3083 want: "a",
3084 }, {
3085 input: "v: w: x: a.b.c, a: b: c: 1",
3086 want: "a.b.c",
3087 }, {
3088 input: "if true { v: w: x: a, a: 1 }",
3089 want: "a",
3090 }, {
3091 input: "v: w: x: w.a.b.c, v: w: a: b: c: 1",
3092 want: "v.w.a.b.c",
3093 }, {
3094 input: `v: w: x: w.a.b.c, v: w: a: b: c: 1, #D: 3, opt?: 3, "v\(#D)": 3, X: {a: 3}, X`,
3095 want: "v.w.a.b.c",
3096 }, {
3097 input: `
3098 v: w: x: w.a[bb]["c"]
3099 v: w: a: b: c: 1
3100 bb: "b"`,
3101 want: "v.w.a.b.c",
3102 }, {
3103 input: `
3104 X="\(y)": 1
3105 v: w: x: X // TODO: Move up for crash
3106 y: "foo"`,
3107 want: "foo",
3108 }, {
3109 input: `
3110 v: w: _
3111 v: [X=string]: x: a[X]
3112 a: w: 1`,
3113 want: "a.w",
3114 }, {
3115 input: `v: {
3116 for t in src {
3117 w: "t\(t)": 1
3118 w: "\(t)": w["t\(t)"]
3119 }
3120 },
3121 src: ["x", "y"]`,
3122 want: "v.w.tx",
3123 }, {
3124 input: `
3125 v: w: x: a
3126 a: 1
3127 for i in [] {
3128 }
3129 `,
3130 want: "a",
3131 }, {
3132 input: `
3133 v: w: close({x: a})
3134 a: 1
3135 `,
3136 want: "a",
3137 }, {
3138 input: `
3139 import "math"
3140
3141 v: w: x: math.Pi
3142 `,
3143 want: "Pi",
3144 wantImportPath: "math",
3145 alt: "3.14159265358979323846264338327950288419716939937510582097494459",
3146 }}
3147 for _, tc := range testCases {
3148 t.Run("", func(t *testing.T) {
3149 var r Runtime
3150 inst, _ := r.Compile("in", tc.input)
3151 v := inst.Lookup("v", "w", "x")
3152
3153 root, path := v.ReferencePath()
3154 if got := path.String(); got != tc.want {
3155 t.Errorf("\n got %v;\nwant %v", got, tc.want)
3156 }
3157 if tc.want != "" {
3158 want := "1"
3159 if tc.alt != "" {
3160 want = tc.alt
3161 }
3162 v := fmt.Sprint(root.LookupPath(path))
3163 if v != want {
3164 t.Errorf("path resolved to %s; want %s", v, want)
3165 }
3166 buildInst := root.BuildInstance()
3167 if buildInst == nil {
3168 t.Fatalf("no build instance found for reference path root")
3169 }
3170 if got, want := buildInst.ImportPath, tc.wantImportPath; got != want {
3171 t.Errorf("unexpected import path; got %q want %q", got, want)
3172 }
3173 }
3174
3175 inst, a := v.Reference()
3176 if got := strings.Join(a, "."); got != tc.want {
3177 t.Errorf("\n got %v;\nwant %v", got, tc.want)
3178 }
3179
3180 if tc.want != "" {
3181 want := "1"
3182 if tc.alt != "" {
3183 want = tc.alt
3184 }
3185 v := fmt.Sprint(inst.Lookup(a...))
3186 if v != want {
3187 t.Errorf("path resolved to %s; want %s", v, want)
3188 }
3189 }
3190 })
3191 }
3192 }
3193
3194 func TestZeroValueBuildInstance(t *testing.T) {
3195 inst := Value{}.BuildInstance()
3196 if inst != nil {
3197 t.Error("unexpected non-nil instance")
3198 }
3199 }
3200
3201 func TestPos(t *testing.T) {
3202 testCases := []struct {
3203 value string
3204 pos string
3205 }{{
3206 value: `
3207 a: string
3208 a: "foo"`,
3209 pos: "3:4",
3210 }, {
3211 value: `
3212 a: x: string
3213 a: x: "x"`,
3214 pos: "2:4",
3215 }, {
3216
3217 value: `
3218 a: [string]: string
3219 a: x: "x"`,
3220 pos: "3:4",
3221 }, {
3222 value: `
3223 a: [string]: [string]: string
3224 a: x: y: "x"`,
3225 pos: "3:4",
3226 }, {
3227 value: `
3228 a: [string]: [string]: [string]: string
3229 a: x: y: z: "x"`,
3230 pos: "3:4",
3231 }}
3232 for _, tc := range testCases {
3233 t.Run("", func(t *testing.T) {
3234 var c Context
3235 (*runtime.Runtime)(&c).Init()
3236 v := c.CompileString(tc.value)
3237 v = v.LookupPath(ParsePath("a"))
3238 pos := v.Pos().String()
3239 if pos != tc.pos {
3240 t.Errorf("got %v; want %v", pos, tc.pos)
3241 }
3242 })
3243 }
3244 }
3245
3246 func TestPathCorrection(t *testing.T) {
3247 testCases := []struct {
3248 input string
3249 lookup func(i *Instance) Value
3250 want string
3251 skip bool
3252 }{{
3253 input: `
3254 a: b: {
3255 c: d: b
3256 }
3257 `,
3258 lookup: func(i *Instance) Value {
3259 op, a := i.Lookup("a", "b", "c", "d").Expr()
3260 _ = op
3261 return a[0]
3262 },
3263 want: "a",
3264 }, {
3265
3266
3267 input: `
3268 a: {
3269 {x: c}
3270 c: 3
3271 }
3272 `,
3273 lookup: func(i *Instance) Value {
3274 op, a := i.Lookup("a").Expr()
3275 _ = op
3276 return a[0].Lookup("x")
3277 },
3278 want: "a.c",
3279 }, {
3280
3281
3282 input: `
3283 a: b: [...T]
3284 a: b: [...T]
3285 T: int
3286 `,
3287 lookup: func(i *Instance) Value {
3288 v, _ := i.Lookup("a", "b").Elem()
3289 _, a := v.Expr()
3290 return a[0]
3291 },
3292 want: "T",
3293 }, {
3294 input: `
3295 #S: {
3296 b?: [...#T]
3297 b?: [...#T]
3298 }
3299 #T: int
3300 `,
3301 lookup: func(i *Instance) Value {
3302 v := i.LookupDef("#S")
3303 f, _ := v.LookupField("b")
3304 v, _ = f.Value.Elem()
3305 _, a := v.Expr()
3306 return a[0]
3307 },
3308 want: "#T",
3309 }, {
3310 input: `
3311 #S: {
3312 a?: [...#T]
3313 b?: [...#T]
3314 }
3315 #T: int
3316 `,
3317 lookup: func(i *Instance) Value {
3318 v := i.LookupDef("#S")
3319 f, _ := v.LookupField("a")
3320 x := f.Value
3321 f, _ = v.LookupField("b")
3322 y := f.Value
3323 u := x.Unify(y)
3324 v, _ = u.Elem()
3325 _, a := v.Expr()
3326 return a[0]
3327 },
3328 want: "#T",
3329 }, {
3330 input: `
3331 #a: {
3332 close({}) | close({c: #T}) | close({d: string})
3333 #T: {b: 3}
3334 }
3335 `,
3336 lookup: func(i *Instance) Value {
3337 f, _ := i.LookupField("#a")
3338 _, a := f.Value.Expr()
3339 _, a = a[0].Expr()
3340 return a[1].Lookup("c")
3341 },
3342 want: "#a.#T",
3343 }, {
3344 input: `
3345 package foo
3346
3347 #Struct: {
3348 #T: int
3349
3350 {b?: #T}
3351 }`,
3352 want: "#Struct.#T",
3353 lookup: func(inst *Instance) Value {
3354
3355 i, _ := inst.Value().Fields(Definitions(true))
3356 if !i.Next() {
3357 t.Fatal("no fields")
3358 }
3359
3360 i, _ = i.Value().Fields(Definitions(true), Optional(true))
3361 if !(i.Next() && i.Next()) {
3362 t.Fatal("no fields")
3363 }
3364 v := i.Value()
3365 return v
3366 },
3367 }, {
3368
3369 input: `
3370 package foo
3371
3372 #A: #B: #T
3373
3374 #T: {
3375 a: #S.#U
3376 #S: #U: {}
3377 }
3378 `,
3379 want: "#T.#S.#U",
3380 lookup: func(inst *Instance) Value {
3381 f, _ := inst.Value().LookupField("#A")
3382 f, _ = f.Value.LookupField("#B")
3383 v := f.Value
3384 v = Dereference(v)
3385 v = v.Lookup("a")
3386 return v
3387 },
3388 }, {
3389
3390
3391 input: `
3392 package foo
3393
3394 #A: #B: #T
3395
3396 #T: {
3397 a: [...#S]
3398 #S: {}
3399 }
3400 `,
3401 want: "#T.#S",
3402 lookup: func(inst *Instance) Value {
3403 f, _ := inst.Value().LookupField("#A")
3404 f, _ = f.Value.LookupField("#B")
3405 v := f.Value
3406 v = Dereference(v)
3407 v, _ = v.Lookup("a").Elem()
3408 return v
3409 },
3410 }, {
3411 input: `
3412 #A: {
3413 b: #T
3414 }
3415
3416 #T: {
3417 a: #S
3418 #S: {}
3419 }
3420 `,
3421 want: "#T.#S",
3422 lookup: func(inst *Instance) Value {
3423 f, _ := inst.Value().LookupField("#A")
3424 v := f.Value.Lookup("b")
3425 v = Dereference(v)
3426 v = v.Lookup("a")
3427 return v
3428 },
3429 }, {
3430 input: `
3431 #Tracing: {
3432 #T: { address?: string }
3433 #S: { ip?: string }
3434
3435 close({}) | close({
3436 t: #T
3437 }) | close({
3438 s: #S
3439 })
3440 }
3441 #X: {}
3442 #X // Disconnect top-level struct from the one visible by close.
3443 `,
3444 want: "#Tracing.#T",
3445 lookup: func(inst *Instance) Value {
3446 f, _ := inst.Value().LookupField("#Tracing")
3447 v := f.Value.Eval()
3448 _, args := v.Expr()
3449 v = args[1]
3450 v = v.Lookup("t")
3451 return v
3452 },
3453 }, {
3454 input: `
3455 x: { if true { v: a } }
3456 a: b
3457 b: 2
3458 `,
3459 want: "b",
3460 lookup: func(inst *Instance) Value {
3461 v := inst.Value().LookupPath(ParsePath("x.v"))
3462 v = Dereference(v)
3463 return v
3464 },
3465 }, {
3466 input: `
3467 package foo
3468
3469 #A:{ if true { #B: #T } }
3470
3471 #T: {
3472 a: #S.#U
3473 #S: #U: {}
3474 }
3475 `,
3476 want: "#T.#S.#U",
3477 lookup: func(inst *Instance) Value {
3478 f, _ := inst.Value().LookupField("#A")
3479 f, _ = f.Value.LookupField("#B")
3480 v := f.Value
3481 v = Dereference(v)
3482 v = v.Lookup("a")
3483 return v
3484 },
3485 }}
3486 for _, tc := range testCases {
3487 if tc.skip {
3488 continue
3489 }
3490 t.Run("", func(t *testing.T) {
3491 var r Runtime
3492 inst, err := r.Compile("in", tc.input)
3493 if err != nil {
3494 t.Fatal(err)
3495 }
3496 v := tc.lookup(inst)
3497 gotInst, ref := v.Reference()
3498 if gotInst != inst {
3499 t.Error("reference not in original instance")
3500 }
3501 gotPath := strings.Join(ref, ".")
3502 if gotPath != tc.want {
3503 t.Errorf("got path %s; want %s", gotPath, tc.want)
3504 }
3505
3506 x, p := v.ReferencePath()
3507 if x.Value() != inst.Value() {
3508 t.Error("reference not in original instance")
3509 }
3510 gotPath = p.String()
3511 if gotPath != tc.want {
3512 t.Errorf("got path %s; want %s", gotPath, tc.want)
3513 }
3514
3515 })
3516 }
3517 }
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578 func checkErr(t *testing.T, err error, str, name string) bool {
3579 t.Helper()
3580 if err == nil {
3581 if str != "" {
3582 t.Errorf(`err:%s: got ""; want %q`, name, str)
3583 }
3584 return true
3585 }
3586 return checkFailed(t, err, str, name)
3587 }
3588
3589 func checkFatal(t *testing.T, err error, str, name string) {
3590 t.Helper()
3591 if !checkFailed(t, err, str, name) {
3592 t.SkipNow()
3593 }
3594 }
3595
3596 func checkFailed(t *testing.T, err error, str, name string) bool {
3597 t.Helper()
3598 if err != nil {
3599 got := err.Error()
3600 if str == "" {
3601 t.Fatalf(`err:%s: got %q; want ""`, name, got)
3602 }
3603 if !strings.Contains(got, str) {
3604 t.Errorf(`err:%s: got %q; want %q`, name, got, str)
3605 }
3606 return false
3607 }
3608 return true
3609 }
3610
3611 func TestExpr(t *testing.T) {
3612 testCases := []struct {
3613 input string
3614 want string
3615 }{{
3616 input: "v: 3",
3617 want: "3",
3618 }, {
3619 input: "v: 3 + 4",
3620 want: "+(3 4)",
3621 }, {
3622 input: "v: !a, a: bool",
3623 want: `!(.(〈〉 "a"))`,
3624 }, {
3625 input: "v: !a, a: 3",
3626 want: `!(.(〈〉 "a"))`,
3627 }, {
3628 input: "v: 1 | 2 | 3 | *4",
3629 want: "|(1 2 3 4)",
3630 }, {
3631 input: "v: 2 & 5",
3632 want: "&(2 5)",
3633 }, {
3634 input: "v: 2 | 5",
3635 want: "|(2 5)",
3636 }, {
3637 input: "v: 2 && 5",
3638 want: "&&(2 5)",
3639 }, {
3640 input: "v: 2 || 5",
3641 want: "||(2 5)",
3642 }, {
3643 input: "v: 2 == 5",
3644 want: "==(2 5)",
3645 }, {
3646 input: "v: !b, b: true",
3647 want: `!(.(〈〉 "b"))`,
3648 }, {
3649 input: "v: 2 != 5",
3650 want: "!=(2 5)",
3651 }, {
3652 input: "v: <5",
3653 want: "<(5)",
3654 }, {
3655 input: "v: 2 <= 5",
3656 want: "<=(2 5)",
3657 }, {
3658 input: "v: 2 > 5",
3659 want: ">(2 5)",
3660 }, {
3661 input: "v: 2 >= 5",
3662 want: ">=(2 5)",
3663 }, {
3664 input: "v: 2 =~ 5",
3665 want: "=~(2 5)",
3666 }, {
3667 input: "v: 2 !~ 5",
3668 want: "!~(2 5)",
3669 }, {
3670 input: "v: 2 + 5",
3671 want: "+(2 5)",
3672 }, {
3673 input: "v: 2 - 5",
3674 want: "-(2 5)",
3675 }, {
3676 input: "v: 2 * 5",
3677 want: "*(2 5)",
3678 }, {
3679 input: "v: 2 / 5",
3680 want: "/(2 5)",
3681 }, {
3682 input: "v: 2 quo 5",
3683 want: "quo(2 5)",
3684 }, {
3685 input: "v: 2 rem 5",
3686 want: "rem(2 5)",
3687 }, {
3688 input: "v: 2 div 5",
3689 want: "div(2 5)",
3690 }, {
3691 input: "v: 2 mod 5",
3692 want: "mod(2 5)",
3693 }, {
3694 input: "v: a.b, a: b: 4",
3695 want: `.(.(〈〉 "a") "b")`,
3696 }, {
3697 input: `v: a["b"], a: b: 3 `,
3698 want: `[](.(〈〉 "a") "b")`,
3699 }, {
3700 input: "v: a[2:5], a: [1, 2, 3, 4, 5]",
3701 want: `[:](.(〈〉 "a") 2 5)`,
3702 }, {
3703 input: "v: len([])",
3704 want: "()(len [])",
3705 }, {
3706 input: "v: a.b, a: { b: string }",
3707 want: `.(.(〈〉 "a") "b")`,
3708 }, {
3709 input: `v: "Hello, \(x)! Welcome to \(place)", place: string, x: string`,
3710 want: `\()("Hello, " .(〈〉 "x") "! Welcome to " .(〈〉 "place") "")`,
3711 }, {
3712
3713
3714 input: `v: { a, #b: 1 }, a: 2`,
3715 want: `&(.(〈〉 "a") {int,#b:1})`,
3716 }, {
3717
3718 input: `v: { a, b: 1 }, a: 2`,
3719 want: `&(.(〈〉 "a") {b:1})`,
3720 }, {
3721
3722 input: `v: { "foo", #def: 1 }`,
3723 want: `{"foo",#def:1}`,
3724 }, {
3725 input: `v: { {} | { a: #A, b: #B}, #A: {} | { c: int} }, #B: int | bool`,
3726 want: `&(|({} {a:#A,b:#B}) {#A:({}|{c:int})})`,
3727 }, {
3728 input: `v: { {c: a}, b: a }, a: int`,
3729 want: `&({c:a} {b:a})`,
3730 }, {
3731 input: `v: [...number] | *[1, 2, 3]`,
3732
3733 want: `[...number]`,
3734 }, {
3735 input: `v: or([1, 2, 3])`,
3736 want: `|(1 2 3)`,
3737 }, {
3738 input: `v: or([])`,
3739 want: `_|_(empty list in call to or)`,
3740 }, {
3741 input: `v: and([1, 2, 3])`,
3742 want: `&(1 2 3)`,
3743 }, {
3744 input: `v: and([])`,
3745 want: `_`,
3746 }, {
3747
3748 input: `
3749 x: *4 | int
3750 v: x | *7
3751 `,
3752 want: `|(.(〈〉 "x") 7)`,
3753 }, {
3754
3755
3756 input: `v: {>30}`,
3757 want: `>(30)`,
3758 }, {
3759 input: `v: {>30, <40}`,
3760 want: `&(>(30) <(40))`,
3761 }, {
3762 input: `a: string, if true { v: a }`,
3763 want: `.(〈〉 "a")`,
3764 }}
3765 for _, tc := range testCases {
3766 t.Run(tc.input, func(t *testing.T) {
3767 v := getInstance(t, tc.input).Lookup("v")
3768 got := exprStr(v)
3769 if got != tc.want {
3770 t.Errorf("\n got %v;\nwant %v", got, tc.want)
3771 }
3772 })
3773 }
3774 }
3775
3776 func exprStr(v Value) string {
3777 op, operands := v.Expr()
3778 if op == NoOp {
3779 return compactRawStr(operands[0])
3780 }
3781 s := op.String()
3782 s += "("
3783 for i, v := range operands {
3784 if i > 0 {
3785 s += " "
3786 }
3787 s += exprStr(v)
3788 }
3789 s += ")"
3790 return s
3791 }
3792
3793 func compactRawStr(v Value) string {
3794 ctx := v.ctx()
3795 cfg := &debug.Config{Compact: true, Raw: true}
3796 return debug.NodeString(ctx, v.v, cfg)
3797 }
3798
View as plain text