1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package adt_test
16
17 import (
18 "fmt"
19 "path/filepath"
20 "strings"
21 "testing"
22
23 "cuelang.org/go/cue/format"
24 "cuelang.org/go/internal/core/adt"
25 "cuelang.org/go/internal/core/debug"
26 "cuelang.org/go/internal/core/eval"
27 "cuelang.org/go/internal/core/export"
28 "cuelang.org/go/internal/core/runtime"
29 "cuelang.org/go/internal/cuetest"
30 )
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 func TestCloseContext(t *testing.T) {
46 r := runtime.New()
47 ctx := eval.NewContext(r, nil)
48
49 v := func(s string) adt.Value {
50 v, _ := r.Compile(nil, s)
51 if err := v.Err(ctx); err != nil {
52 t.Fatal(err.Err)
53 }
54 v.Finalize(ctx)
55 return v.Value()
56 }
57 ref := func(s string) adt.Expr {
58 f := r.StrLabel(s)
59 return &adt.FieldReference{Label: f}
60 }
61
62
63
64
65
66
67
68
69
70
71
72
73 type testCase struct {
74 name string
75 run func(*adt.FieldTester)
76
77
78 arcs string
79
80
81 patterns string
82
83
84 allowed string
85
86
87 err string
88 }
89 cases := []testCase{{
90 name: "one",
91 run: func(x *adt.FieldTester) {
92 x.Run(x.Field("a", "foo"))
93 },
94 arcs: `a: {"foo"}`,
95 }, {
96 name: "two",
97 run: func(x *adt.FieldTester) {
98 x.Run(
99 x.Field("a", "foo"),
100 x.Field("a", "bar"),
101 )
102 },
103 arcs: `a: {"foo" "bar"}`,
104 }, {
105
106
107
108 name: "double nested",
109
110
111
112
113
114
115
116
117
118
119 run: func(x *adt.FieldTester) {
120 x.Run(
121 x.Field("a", "foo"),
122 x.EmbedDef(
123 x.EmbedDef(x.Field("a", "foo")),
124 x.Field("b", "foo"),
125 ),
126 )
127 },
128 arcs: `
129 a: {
130 "foo"
131 [e]{
132 [d]{
133 [e]{
134 [d]{"foo"}
135 }
136 }
137 }
138 }
139 b: {
140 [e]{
141 [d]{"foo"}
142 }
143 }`,
144 }, {
145 name: "single pattern",
146 run: func(x *adt.FieldTester) {
147 x.Run(x.Pat(v(`<="foo"`), v("1")))
148 },
149 arcs: "",
150 patterns: `<="foo": {1}`,
151 allowed: `<="foo"`,
152 }, {
153 name: "total patterns",
154 run: func(x *adt.FieldTester) {
155 x.Run(
156 x.Pat(v(`<="foo"`), x.NewString("foo")),
157 x.Pat(v(`string`), v("1")),
158 x.Pat(v(`string`), v("1")),
159 x.Pat(v(`<="foo"`), v("2")),
160 )
161 },
162
163 arcs: "",
164 patterns: `
165 <="foo": {"foo" 2}
166 string: {1 1}`,
167
168
169 allowed: "",
170 }, {
171 name: "multi patterns",
172 run: func(x *adt.FieldTester) {
173 shared := v("100")
174 x.Run(
175 x.Pat(v(`<="foo"`), x.NewString("foo")),
176 x.Pat(v(`>"bar"`), shared),
177 x.Pat(v(`>"bar"`), shared),
178 x.Pat(v(`<="foo"`), v("1")),
179 )
180 },
181
182
183 patterns: `
184 <="foo": {"foo" 1}
185 >"bar": {100}`,
186
187
188 allowed: `|(<="foo", >"bar")`,
189 }, {
190 name: "pattern defined after matching field",
191 run: func(x *adt.FieldTester) {
192 x.Run(
193 x.Field("a", "foo"),
194 x.Pat(v(`string`), v(`string`)),
195 )
196 },
197 arcs: `a: {"foo" string}`,
198 patterns: `string: {string}`,
199 allowed: "",
200 }, {
201 name: "pattern defined before matching field",
202 run: func(x *adt.FieldTester) {
203 x.Run(
204 x.Pat(v(`string`), v(`string`)),
205 x.Field("a", "foo"),
206 )
207 },
208 arcs: `a: {"foo" string}`,
209 patterns: `string: {string}`,
210 allowed: "",
211 }, {
212 name: "shared on one level",
213 run: func(x *adt.FieldTester) {
214 shared := ref("shared")
215 x.Run(
216 x.Pat(v(`string`), v(`1`)),
217 x.Pat(v(`>"a"`), shared),
218 x.Pat(v(`>"a"`), shared),
219 x.Field("m", "foo"),
220 x.Pat(v(`string`), v(`2`)),
221 x.Pat(v(`>"a"`), shared),
222 x.Pat(v(`>"b"`), shared),
223 )
224 },
225 arcs: `m: {"foo" 1 shared 2}`,
226 patterns: `
227 string: {1 2}
228 >"a": {shared}
229 >"b": {shared}`,
230 }, {
231
232
233 name: "do not share between groups",
234 run: func(x *adt.FieldTester) {
235 notShared := ref("notShared")
236 x.Run(
237 x.Def(x.Field("m", notShared)),
238
239
240
241
242 x.Field("m", notShared),
243
244
245
246
247 x.Def(x.Field("m", notShared)),
248
249
250 x.Embed(x.Field("m", notShared)),
251 )
252 },
253 arcs: `m: {
254 [d]{notShared}
255 notShared
256 [d]{notShared}
257 [e]{notShared}
258 }`,
259 }, {
260 name: "conjunction of patterns",
261 run: func(x *adt.FieldTester) {
262 x.Run(
263 x.Def(x.Pat(v(`>"a"`), v("1"))),
264 x.Def(x.Pat(v(`<"m"`), v("2"))),
265 )
266 },
267 patterns: `
268 >"a": {1}
269 <"m": {2}`,
270
271 allowed: `&(>"a", <"m")`,
272 }, {
273 name: "pattern in definition in embedding",
274 run: func(x *adt.FieldTester) {
275 x.Run(
276 x.Embed(x.Def(x.Pat(v(`>"a"`), v("1")))),
277 )
278 },
279 patterns: `>"a": {1}`,
280 allowed: `>"a"`,
281 }, {
282 name: "avoid duplicate pattern entries",
283 run: func(x *adt.FieldTester) {
284 x.Run(
285 x.Field("b", "bar"),
286 x.Field("b", "baz"),
287 x.Pat(v(`string`), v("2")),
288 x.Pat(v(`string`), v("3")),
289 x.Field("c", "bar"),
290 x.Field("c", "baz"),
291 )
292 },
293 arcs: `
294 b: {"bar" "baz" 2 3}
295 c: {"bar" 2 3 "baz"}`,
296
297 patterns: `string: {2 3}`,
298 }, {
299 name: "conjunction in embedding",
300 run: func(x *adt.FieldTester) {
301 x.Run(
302 x.Field("b", "foo"),
303 x.Embed(
304 x.Def(x.Pat(v(`>"a"`), v("1"))),
305 x.Def(x.Pat(v(`<"z"`), v("2"))),
306 ),
307 x.Field("c", "bar"),
308 x.Pat(v(`<"q"`), v("3")),
309 )
310 },
311 arcs: `
312 b: {
313 "foo"
314 [e]{
315 [d]{1}
316 [d]{2}
317 }
318 3
319 }
320 c: {
321 "bar"
322 [ed]{
323 [d]{1}
324 [d]{2}
325 }
326 3
327 }`,
328 patterns: `
329 >"a": {1}
330 <"z": {2}
331 <"q": {3}`,
332 allowed: `|(<"q", &(>"a", <"z"))`,
333 }, {
334
335
336 name: "conjunctions in embedding 1",
337 run: func(x *adt.FieldTester) {
338 x.Run(
339 x.Def(
340 x.Embed(
341 x.Def(x.Pat(v(`=~"^[a-s]*$"`), v("3"))),
342 x.Def(x.Pat(v(`=~"^xxx$"`), v("4"))),
343 ),
344 x.Pat(v(`=~"^b*$"`), v("4")),
345 ),
346 x.Field("b", v("4")),
347 )
348 },
349 arcs: `b: {
350 4
351 [d]{
352 [ed]{
353 [d]{3}
354 }
355 4
356 }
357 }`,
358 err: ``,
359 patterns: `
360 =~"^[a-s]*$": {3}
361 =~"^xxx$": {4}
362 =~"^b*$": {4}`, allowed: `|(=~"^b*$", &(=~"^[a-s]*$", =~"^xxx$"))`,
363 }, {
364 name: "conjunctions in embedding 2",
365 run: func(x *adt.FieldTester) {
366 x.Run(
367 x.Embed(
368 x.Field("b", v("4")),
369 x.Embed(
370 x.Def(x.Pat(v(`>"b"`), v("3"))),
371 x.Def(x.Pat(v(`<"h"`), v("4"))),
372 ),
373 ),
374 )
375 },
376 arcs: `b: {
377 [e]{
378 4
379 [ed]{
380 [d]{4}
381 }
382 }
383 }`,
384
385 err: ``,
386 patterns: `
387 >"b": {3}
388 <"h": {4}`,
389 }, {
390 name: "conjunctions in embedding 3",
391 run: func(x *adt.FieldTester) {
392 x.Run(
393 x.Embed(
394 x.Def(
395 x.Field("b", "foo"),
396 x.Embed(
397 x.Def(x.Pat(v(`>"a"`), v("1"))),
398 x.Def(x.Pat(v(`<"g"`), v("2"))),
399 x.Pat(v(`<"z"`), v("3")),
400 ),
401 x.Embed(
402 x.Def(x.Pat(v(`>"b"`), v("3"))),
403 x.Def(x.Pat(v(`<"h"`), v("4"))),
404 ),
405 x.Field("c", "bar"),
406 x.Pat(v(`<"q"`), v("5")),
407 ),
408 x.Def(
409 x.Embed(
410 x.Def(x.Pat(v(`>"m"`), v("6"))),
411 x.Def(x.Pat(v(`<"y"`), v("7"))),
412 x.Pat(v(`<"z"`), v("8")),
413 ),
414 x.Embed(
415 x.Def(x.Pat(v(`>"n"`), v("9"))),
416 x.Def(x.Pat(v(`<"z"`), v("10"))),
417 ),
418 x.Field("c", "bar"),
419 x.Pat(v(`<"q"`), v("11")),
420 ),
421 ),
422 x.Pat(v(`<"h"`), v("12")),
423 )
424 },
425 arcs: `
426 b: {
427 [e]{
428 [d]{
429 "foo"
430 [e]{
431 [d]{1}
432 [d]{2}
433 3
434 }
435 [ed]{
436 [d]{4}
437 }
438 5
439 }
440 [d]{
441 [ed]{
442 [d]{7}
443 8
444 }
445 [ed]{
446 [d]{10}
447 }
448 11
449 }
450 }
451 12
452 }
453 c: {
454 [e]{
455 [d]{
456 "bar"
457 [ed]{
458 [d]{1}
459 [d]{2}
460 3
461 }
462 [ed]{
463 [d]{3}
464 [d]{4}
465 }
466 5
467 }
468 [d]{
469 [ed]{
470 [d]{7}
471 8
472 }
473 [ed]{
474 [d]{10}
475 }
476 "bar"
477 11
478 }
479 }
480 12
481 }`,
482 patterns: `
483 >"a": {1}
484 <"g": {2}
485 <"z": {3 8 10}
486 >"b": {3}
487 <"h": {4 12}
488 <"q": {5 11}
489 >"m": {6}
490 <"y": {7}
491 >"n": {9}`,
492 allowed: `|(<"h", &(|(<"q", &(>"b", <"h"), &(>"a", <"g")), |(<"q", &(>"n", <"z"), &(>"m", <"y"))))`,
493 }, {
494 name: "dedup equal",
495 run: func(x *adt.FieldTester) {
496 shared := v("1")
497 x.Run(
498 x.FieldDedup("a", shared),
499 x.FieldDedup("a", shared),
500 )
501 },
502 patterns: "",
503 allowed: "",
504 arcs: `a: {1}`,
505 }, {
506
507 name: "disallowed before",
508 run: func(x *adt.FieldTester) {
509 x.Run(
510 x.Field("a", v("1")),
511 x.Def(x.Field("b", v("1"))),
512 )
513 },
514 arcs: `
515 a: {1}
516 b: {
517 [d]{1}
518 }`,
519 err: `a: field not allowed`,
520 }, {
521
522 name: "disallowed after",
523 run: func(x *adt.FieldTester) {
524 x.Run(
525 x.Def(x.Field("b", v("1"))),
526 x.Field("a", v("1")),
527 )
528 },
529 arcs: `
530 b: {
531 [d]{1}
532 }
533 a: {1}`,
534 err: `a: field not allowed`,
535 }, {
536
537
538
539 name: "def embed",
540 run: func(x *adt.FieldTester) {
541 x.Run(
542 x.Group(x.EmbedDef(x.Field("b", "foo"))),
543 x.Field("c", "bar"),
544 )
545 },
546 arcs: `
547 b: {
548 []{
549 [e]{
550 [d]{"foo"}
551 }
552 }
553 }
554 c: {"bar"}`,
555 err: `c: field not allowed`,
556 }, {
557
558
559
560 name: "def embed",
561 run: func(x *adt.FieldTester) {
562 x.Run(
563 x.Group(x.EmbedDef(x.Field("b", "foo")),
564 x.Field("c", "foo"),
565 ),
566 x.Field("d", "bar"),
567 )
568 },
569 arcs: `
570 b: {
571 []{
572 [e]{
573 [d]{"foo"}
574 }
575 }
576 }
577 c: {
578 [d]{"foo"}
579 }
580 d: {"bar"}`,
581 err: `d: field not allowed`,
582 }, {
583
584 name: "X",
585 run: func(x *adt.FieldTester) {
586 x.Run(
587 x.Field("b", "foo"),
588 x.Embed(
589 x.Def(x.Pat(v(`>"b"`), v("3"))),
590 x.Def(x.Pat(v(`<"h"`), v("4"))),
591 ),
592 )
593 },
594
595 arcs: `b: {
596 "foo"
597 [ed]{
598 [d]{4}
599 }
600 }`,
601 err: ``,
602 patterns: `
603 >"b": {3}
604 <"h": {4}`,
605 allowed: `&(>"b", <"h")`,
606 }}
607
608 cuetest.Run(t, cases, func(t *cuetest.T, tc *testCase) {
609
610
611
612
613
614 adt.DebugDeps = true
615 showGraph := false
616 x := adt.NewFieldTester(r)
617 tc.run(x)
618
619 ctx := x.OpContext
620
621 switch graph, hasError := adt.CreateMermaidGraph(ctx, x.Root, true); {
622 case !hasError:
623 case showGraph:
624 path := filepath.Join(".debug", "TestCloseContext", tc.name)
625 adt.OpenNodeGraph(tc.name, path, "in", "out", graph)
626 fallthrough
627 default:
628 t.Errorf("imbalanced counters")
629 }
630
631 t.Equal(writeArcs(x, x.Root), tc.arcs)
632 t.Equal(x.Error(), tc.err)
633
634 var patterns, allowed string
635 if pcs := x.Root.PatternConstraints; pcs != nil {
636 patterns = writePatterns(x, pcs.Pairs)
637 if pcs.Allowed != nil {
638 allowed = debug.NodeString(r, pcs.Allowed, nil)
639
640
641
642 }
643 }
644
645 t.Equal(patterns, tc.patterns)
646 t.Equal(allowed, tc.allowed)
647 })
648 }
649
650 const (
651 initialIndent = 3
652 indentString = "\t"
653 )
654
655 func writeArcs(x adt.Runtime, v *adt.Vertex) string {
656 b := &strings.Builder{}
657 for _, a := range v.Arcs {
658 if len(v.Arcs) > 1 {
659 fmt.Fprint(b, "\n", strings.Repeat(indentString, initialIndent))
660 }
661 fmt.Fprintf(b, "%s: ", a.Label.RawString(x))
662
663
664
665 if len(a.Conjuncts) != 1 {
666 panic("unexpected conjunct length")
667 }
668 g := a.Conjuncts[0].Elem().(*adt.ConjunctGroup)
669 vertexString(x, b, *g, initialIndent)
670 }
671 return b.String()
672 }
673
674 func writePatterns(x adt.Runtime, pairs []adt.PatternConstraint) string {
675 b := &strings.Builder{}
676 for _, pc := range pairs {
677 if len(pairs) > 1 {
678 fmt.Fprint(b, "\n", strings.Repeat(indentString, initialIndent))
679 }
680 b.WriteString(pExpr(x, pc.Pattern))
681 b.WriteString(": ")
682 vertexString(x, b, pc.Constraint.Conjuncts, initialIndent)
683 }
684 return b.String()
685 }
686
687 func hasVertex(a []adt.Conjunct) bool {
688 for _, c := range a {
689 switch c.Elem().(type) {
690 case *adt.ConjunctGroup:
691 return true
692 case *adt.Vertex:
693 return true
694 }
695 }
696 return false
697 }
698
699 func vertexString(x adt.Runtime, b *strings.Builder, a []adt.Conjunct, indent int) {
700 hasVertex := hasVertex(a)
701
702 b.WriteString("{")
703 for i, c := range a {
704 if g, ok := c.Elem().(*adt.ConjunctGroup); ok {
705 if hasVertex {
706 doIndent(b, indent+1)
707 }
708 b.WriteString("[")
709 if c.CloseInfo.FromEmbed {
710 b.WriteString("e")
711 }
712 if c.CloseInfo.FromDef {
713 b.WriteString("d")
714 }
715 b.WriteString("]")
716 vertexString(x, b, *g, indent+1)
717 } else {
718 if hasVertex {
719 doIndent(b, indent+1)
720 } else if i > 0 {
721 b.WriteString(" ")
722 }
723 b.WriteString(pExpr(x, c.Expr()))
724 }
725 }
726 if hasVertex {
727 doIndent(b, indent)
728 }
729 b.WriteString("}")
730 }
731
732 func doIndent(b *strings.Builder, indent int) {
733 fmt.Fprint(b, "\n", strings.Repeat(indentString, indent))
734 }
735
736 func pExpr(x adt.Runtime, e adt.Expr) string {
737 a, _ := export.All.Expr(x, "test", e)
738 b, _ := format.Node(a)
739 return string(b)
740 }
741
View as plain text