1 package d2ir_test
2
3 import (
4 "testing"
5
6 "oss.terrastruct.com/util-go/assert"
7 )
8
9 func testCompilePatterns(t *testing.T) {
10 t.Parallel()
11
12 tca := []testCase{
13 {
14 name: "escaped",
15 run: func(t testing.TB) {
16 m, err := compile(t, `animal: meow
17 action: yes
18 a\*: globbed`)
19 assert.Success(t, err)
20 assertQuery(t, m, 3, 0, nil, "")
21 assertQuery(t, m, 0, 0, "meow", "animal")
22 assertQuery(t, m, 0, 0, "yes", "action")
23 assertQuery(t, m, 0, 0, "globbed", `a\*`)
24 },
25 },
26 {
27 name: "prefix",
28 run: func(t testing.TB) {
29 m, err := compile(t, `animal: meow
30 action: yes
31 a*: globbed`)
32 assert.Success(t, err)
33 assertQuery(t, m, 2, 0, nil, "")
34 assertQuery(t, m, 0, 0, "globbed", "animal")
35 assertQuery(t, m, 0, 0, "globbed", "action")
36 },
37 },
38 {
39 name: "case/1",
40 run: func(t testing.TB) {
41 m, err := compile(t, `animal: meow
42 action: yes
43 A*: globbed`)
44 assert.Success(t, err)
45 assertQuery(t, m, 2, 0, nil, "")
46 assertQuery(t, m, 0, 0, "globbed", "animal")
47 assertQuery(t, m, 0, 0, "globbed", "action")
48 },
49 },
50 {
51 name: "case/2",
52 run: func(t testing.TB) {
53 m, err := compile(t, `diddy kong
54 Donkey Kong
55 *kong: yes`)
56 assert.Success(t, err)
57 assertQuery(t, m, 2, 0, nil, "")
58 assertQuery(t, m, 0, 0, "yes", "diddy kong")
59 assertQuery(t, m, 0, 0, "yes", "Donkey Kong")
60 },
61 },
62 {
63 name: "suffix",
64 run: func(t testing.TB) {
65 m, err := compile(t, `animal: meow
66 jingle: loud
67 *l: globbed`)
68 assert.Success(t, err)
69 assertQuery(t, m, 2, 0, nil, "")
70 assertQuery(t, m, 0, 0, "globbed", "animal")
71 assertQuery(t, m, 0, 0, "globbed", "jingle")
72 },
73 },
74 {
75 name: "prefix-suffix",
76 run: func(t testing.TB) {
77 m, err := compile(t, `tinker: meow
78 thinker: yes
79 t*r: globbed`)
80 assert.Success(t, err)
81 assertQuery(t, m, 2, 0, nil, "")
82 assertQuery(t, m, 0, 0, "globbed", "tinker")
83 assertQuery(t, m, 0, 0, "globbed", "thinker")
84 },
85 },
86 {
87 name: "prefix-suffix/2",
88 run: func(t testing.TB) {
89 m, err := compile(t, `tinker: meow
90 thinker: yes
91 t*ink*r: globbed`)
92 assert.Success(t, err)
93 assertQuery(t, m, 2, 0, nil, "")
94 assertQuery(t, m, 0, 0, "globbed", "tinker")
95 assertQuery(t, m, 0, 0, "globbed", "thinker")
96 },
97 },
98 {
99 name: "prefix-suffix/3",
100 run: func(t testing.TB) {
101 m, err := compile(t, `tinkertinker: meow
102 thinkerthinker: yes
103 t*ink*r*t*inke*: globbed`)
104 assert.Success(t, err)
105 assertQuery(t, m, 2, 0, nil, "")
106 assertQuery(t, m, 0, 0, "globbed", "tinkertinker")
107 assertQuery(t, m, 0, 0, "globbed", "thinkerthinker")
108 },
109 },
110 {
111 name: "nested/prefix-suffix/3",
112 run: func(t testing.TB) {
113 m, err := compile(t, `animate.constant.tinkertinker: meow
114 astronaut.constant.thinkerthinker: yes
115 a*n*t*.constant.t*ink*r*t*inke*: globbed`)
116 assert.Success(t, err)
117 assertQuery(t, m, 6, 0, nil, "")
118 assertQuery(t, m, 0, 0, "globbed", "animate.constant.tinkertinker")
119 assertQuery(t, m, 0, 0, "globbed", "astronaut.constant.thinkerthinker")
120 },
121 },
122 {
123 name: "edge/1",
124 run: func(t testing.TB) {
125 m, err := compile(t, `animate
126 animal
127 an* -> an*`)
128 assert.Success(t, err)
129 assertQuery(t, m, 2, 2, nil, "")
130 assertQuery(t, m, 0, 0, nil, "(animate -> animal)[0]")
131 assertQuery(t, m, 0, 0, nil, "(animal -> animate)[0]")
132 },
133 },
134 {
135 name: "edge/2",
136 run: func(t testing.TB) {
137 m, err := compile(t, `shared.animate
138 shared.animal
139 sh*.(an* -> an*)`)
140 assert.Success(t, err)
141 assertQuery(t, m, 3, 2, nil, "")
142 assertQuery(t, m, 2, 2, nil, "shared")
143 assertQuery(t, m, 0, 0, nil, "shared.(animate -> animal)[0]")
144 assertQuery(t, m, 0, 0, nil, "shared.(animal -> animate)[0]")
145 },
146 },
147 {
148 name: "edge/3",
149 run: func(t testing.TB) {
150 m, err := compile(t, `shared.animate
151 shared.animal
152 sh*.an* -> sh*.an*`)
153 assert.Success(t, err)
154 assertQuery(t, m, 3, 2, nil, "")
155 assertQuery(t, m, 2, 2, nil, "shared")
156 assertQuery(t, m, 0, 0, nil, "shared.(animate -> animal)[0]")
157 assertQuery(t, m, 0, 0, nil, "shared.(animal -> animate)[0]")
158 },
159 },
160 {
161 name: "edge-glob-index",
162 run: func(t testing.TB) {
163 m, err := compile(t, `a -> b
164 a -> b
165 a -> b
166 (a -> b)[*].style.fill: red
167 `)
168 assert.Success(t, err)
169 assertQuery(t, m, 8, 3, nil, "")
170 assertQuery(t, m, 0, 0, "red", "(a -> b)[0].style.fill")
171 assertQuery(t, m, 0, 0, "red", "(a -> b)[1].style.fill")
172 assertQuery(t, m, 0, 0, "red", "(a -> b)[2].style.fill")
173 },
174 },
175 {
176 name: "glob-edge-glob-index",
177 run: func(t testing.TB) {
178 m, err := compile(t, `a -> b
179 a -> b
180 a -> b
181 c -> b
182 (* -> b)[*].style.fill: red
183 `)
184 assert.Success(t, err)
185 assertQuery(t, m, 11, 4, nil, "")
186 assertQuery(t, m, 0, 0, "red", "(a -> b)[0].style.fill")
187 assertQuery(t, m, 0, 0, "red", "(a -> b)[1].style.fill")
188 assertQuery(t, m, 0, 0, "red", "(a -> b)[2].style.fill")
189 assertQuery(t, m, 0, 0, "red", "(c -> b)[0].style.fill")
190 },
191 },
192 {
193 name: "edge-nexus",
194 run: func(t testing.TB) {
195 m, err := compile(t, `a
196 b
197 c
198 d
199 * -> nexus
200 `)
201 assert.Success(t, err)
202 assertQuery(t, m, 5, 4, nil, "")
203 assertQuery(t, m, 0, 0, nil, "(a -> nexus)[0]")
204 assertQuery(t, m, 0, 0, nil, "(b -> nexus)[0]")
205 assertQuery(t, m, 0, 0, nil, "(c -> nexus)[0]")
206 assertQuery(t, m, 0, 0, nil, "(d -> nexus)[0]")
207 },
208 },
209 {
210 name: "double-glob/1",
211 run: func(t testing.TB) {
212 m, err := compile(t, `shared.animate
213 shared.animal
214 **.style.fill: red`)
215 assert.Success(t, err)
216 assertQuery(t, m, 9, 0, nil, "")
217 assertQuery(t, m, 8, 0, nil, "shared")
218 assertQuery(t, m, 1, 0, nil, "shared.style")
219 assertQuery(t, m, 2, 0, nil, "shared.animate")
220 assertQuery(t, m, 1, 0, nil, "shared.animate.style")
221 assertQuery(t, m, 2, 0, nil, "shared.animal")
222 assertQuery(t, m, 1, 0, nil, "shared.animal.style")
223 },
224 },
225 {
226 name: "double-glob/edge-no-container",
227 run: func(t testing.TB) {
228 m, err := compile(t, `zone A: {
229 machine A
230 machine B: {
231 submachine A
232 submachine B
233 }
234 }
235 zone A.** -> load balancer
236 `)
237 assert.Success(t, err)
238 assertQuery(t, m, 6, 3, nil, "")
239 },
240 },
241 {
242 name: "reserved",
243 run: func(t testing.TB) {
244 m, err := compile(t, `vars: {
245 d2-config: {
246 layout-engine: elk
247 }
248 }
249
250 Spiderman 1
251 Spiderman 2
252 Spiderman 3
253
254 * -> *: arrow`)
255 assert.Success(t, err)
256 assertQuery(t, m, 6, 6, nil, "")
257 assertQuery(t, m, 0, 0, "arrow", "(* -> *)[*]")
258 },
259 },
260 {
261 name: "scenarios",
262 run: func(t testing.TB) {
263 m, err := compile(t, `
264
265 scenarios: {
266 meow: {
267 e
268 f
269 g
270 h
271 }
272 }
273
274 a
275 b
276 c
277 d
278
279 **: something
280 ** -> **
281 `)
282 assert.Success(t, err)
283 assertQuery(t, m, 10, 24, nil, "")
284 assertQuery(t, m, 0, 0, "something", "**")
285 assertQuery(t, m, 0, 0, nil, "(* -> *)[*]")
286 },
287 },
288 {
289 name: "single-glob/defaults",
290 run: func(t testing.TB) {
291 m, err := compile(t, `wrapper.*: {
292 shape: page
293 }
294
295 wrapper.a
296 wrapper.b
297 wrapper.c
298 wrapper.d
299
300 scenarios.x: { wrapper.p }
301 layers.x: { wrapper.p }
302 `)
303 assert.Success(t, err)
304 assertQuery(t, m, 26, 0, nil, "")
305 assertQuery(t, m, 0, 0, "page", "wrapper.a.shape")
306 assertQuery(t, m, 0, 0, "page", "wrapper.b.shape")
307 assertQuery(t, m, 0, 0, "page", "wrapper.c.shape")
308 assertQuery(t, m, 0, 0, "page", "wrapper.d.shape")
309 assertQuery(t, m, 0, 0, "page", "scenarios.x.wrapper.p.shape")
310 assertQuery(t, m, 0, 0, nil, "layers.x.wrapper.p")
311 },
312 },
313 {
314 name: "double-glob/edge/1",
315 run: func(t testing.TB) {
316 m, err := compile(t, `fast: {
317 a
318 far
319 }
320
321 task: {
322 a
323 }
324
325 task.** -> fast
326 `)
327 assert.Success(t, err)
328 assertQuery(t, m, 5, 1, nil, "")
329 },
330 },
331 {
332 name: "double-glob/edge/2",
333 run: func(t testing.TB) {
334 m, err := compile(t, `a
335
336 **.b -> c
337 `)
338 assert.Success(t, err)
339 assertQuery(t, m, 3, 1, nil, "")
340 },
341 },
342 {
343 name: "double-glob/defaults",
344 run: func(t testing.TB) {
345 m, err := compile(t, `**: {
346 shape: page
347 }
348
349 a
350 b
351 c
352 d
353
354 scenarios.x: { p }
355 layers.x: { p }
356 `)
357 assert.Success(t, err)
358 assertQuery(t, m, 23, 0, nil, "")
359 assertQuery(t, m, 0, 0, "page", "a.shape")
360 assertQuery(t, m, 0, 0, "page", "b.shape")
361 assertQuery(t, m, 0, 0, "page", "c.shape")
362 assertQuery(t, m, 0, 0, "page", "d.shape")
363 assertQuery(t, m, 0, 0, "page", "scenarios.x.p.shape")
364 assertQuery(t, m, 0, 0, nil, "layers.x.p")
365 },
366 },
367 {
368 name: "triple-glob/defaults",
369 run: func(t testing.TB) {
370 m, err := compile(t, `***: {
371 shape: page
372 }
373
374 a
375 b
376 c
377 d
378
379 scenarios.x: { p }
380 layers.x: { p }
381 `)
382 assert.Success(t, err)
383 assertQuery(t, m, 24, 0, nil, "")
384 assertQuery(t, m, 0, 0, "page", "a.shape")
385 assertQuery(t, m, 0, 0, "page", "b.shape")
386 assertQuery(t, m, 0, 0, "page", "c.shape")
387 assertQuery(t, m, 0, 0, "page", "d.shape")
388 assertQuery(t, m, 0, 0, "page", "scenarios.x.p.shape")
389 assertQuery(t, m, 0, 0, "page", "layers.x.p.shape")
390 },
391 },
392 {
393 name: "triple-glob/edge-defaults",
394 run: func(t testing.TB) {
395 m, err := compile(t, `(*** -> ***)[*]: {
396 target-arrowhead.shape: diamond
397 }
398
399 a -> b
400 c -> d
401
402 scenarios.x: { p -> q }
403 layers.x: { j -> f }
404 `)
405 assert.Success(t, err)
406 assertQuery(t, m, 28, 6, nil, "")
407 assertQuery(t, m, 0, 0, "diamond", "(a -> b)[0].target-arrowhead.shape")
408 assertQuery(t, m, 0, 0, "diamond", "(c -> d)[0].target-arrowhead.shape")
409 assertQuery(t, m, 0, 0, "diamond", "scenarios.x.(a -> b)[0].target-arrowhead.shape")
410 assertQuery(t, m, 0, 0, "diamond", "scenarios.x.(c -> d)[0].target-arrowhead.shape")
411 assertQuery(t, m, 0, 0, "diamond", "scenarios.x.(p -> q)[0].target-arrowhead.shape")
412 assertQuery(t, m, 4, 1, nil, "layers.x")
413 assertQuery(t, m, 0, 0, "diamond", "layers.x.(j -> f)[0].target-arrowhead.shape")
414 },
415 },
416 {
417 name: "alixander-review/1",
418 run: func(t testing.TB) {
419 m, err := compile(t, `
420 ***.style.fill: yellow
421 **.shape: circle
422 *.style.multiple: true
423
424 x: {
425 y
426 }
427
428 layers: {
429 next: {
430 a
431 }
432 }
433 `)
434 assert.Success(t, err)
435 assertQuery(t, m, 14, 0, nil, "")
436 },
437 },
438 {
439 name: "alixander-review/2",
440 run: func(t testing.TB) {
441 m, err := compile(t, `
442 a
443
444 * -> y
445
446 b
447 c
448 `)
449 assert.Success(t, err)
450 assertQuery(t, m, 4, 3, nil, "")
451 },
452 },
453 {
454 name: "alixander-review/3",
455 run: func(t testing.TB) {
456 m, err := compile(t, `
457 a
458
459 ***.b -> c
460
461 layers: {
462 z: {
463 d
464 }
465 }
466 `)
467 assert.Success(t, err)
468 assertQuery(t, m, 8, 2, nil, "")
469 },
470 },
471 {
472 name: "alixander-review/4",
473 run: func(t testing.TB) {
474 m, err := compile(t, `
475 **.child
476
477 a
478 b
479 c
480 `)
481 assert.Success(t, err)
482 assertQuery(t, m, 6, 0, nil, "")
483 },
484 },
485 {
486 name: "alixander-review/5",
487 run: func(t testing.TB) {
488 m, err := compile(t, `
489 **.style.fill: red
490
491 scenarios: {
492 b: {
493 a -> b
494 }
495 }
496 `)
497 assert.Success(t, err)
498 assertQuery(t, m, 8, 1, nil, "")
499 assertQuery(t, m, 0, 0, "red", "scenarios.b.a.style.fill")
500 assertQuery(t, m, 0, 0, "red", "scenarios.b.b.style.fill")
501 },
502 },
503 {
504 name: "alixander-review/6",
505 run: func(t testing.TB) {
506 m, err := compile(t, `
507 (* -> *)[*].style.opacity: 0.1
508
509 x -> y: hi
510 x -> y
511 `)
512 assert.Success(t, err)
513 assertQuery(t, m, 6, 2, nil, "")
514 assertQuery(t, m, 0, 0, 0.1, "(x -> y)[0].style.opacity")
515 assertQuery(t, m, 0, 0, 0.1, "(x -> y)[1].style.opacity")
516 },
517 },
518 {
519 name: "alixander-review/7",
520 run: func(t testing.TB) {
521 m, err := compile(t, `
522 *: {
523 style.fill: red
524 }
525 **: {
526 style.fill: red
527 }
528
529 table: {
530 style.fill: blue
531 shape: sql_table
532 a: b
533 }
534 `)
535 assert.Success(t, err)
536 assertQuery(t, m, 7, 0, nil, "")
537 assertQuery(t, m, 0, 0, "blue", "table.style.fill")
538 },
539 },
540 {
541 name: "alixander-review/8",
542 run: func(t testing.TB) {
543 m, err := compile(t, `
544 (a -> *)[*].style.stroke: red
545 (* -> *)[*].style.stroke: red
546
547 b -> c
548 `)
549 assert.Success(t, err)
550 assertQuery(t, m, 4, 1, nil, "")
551 assertQuery(t, m, 0, 0, "red", "(b -> c)[0].style.stroke")
552 },
553 },
554 {
555 name: "override/1",
556 run: func(t testing.TB) {
557 m, err := compile(t, `
558 **.style.fill: yellow
559 **.style.fill: red
560
561 a
562 `)
563 assert.Success(t, err)
564 assertQuery(t, m, 3, 0, nil, "")
565 assertQuery(t, m, 0, 0, "red", "a.style.fill")
566 },
567 },
568 {
569 name: "override/2",
570 run: func(t testing.TB) {
571 m, err := compile(t, `
572 ***.style.fill: yellow
573
574 layers: {
575 hi: {
576 **.style.fill: red
577 # should be red, but it's yellow right now
578 a
579 }
580 }
581 `)
582 assert.Success(t, err)
583 assertQuery(t, m, 5, 0, nil, "")
584 assertQuery(t, m, 0, 0, "red", "layers.hi.a.style.fill")
585 },
586 },
587 {
588 name: "override/3",
589 run: func(t testing.TB) {
590 m, err := compile(t, `
591 (*** -> ***)[*].label: hi
592
593 a -> b
594
595 layers: {
596 hi: {
597 (*** -> ***)[*].label: bye
598
599 scenarios: {
600 b: {
601 # This label is "hi", but it should be "bye"
602 a -> b
603 }
604 }
605 }
606 }
607 `)
608 assert.Success(t, err)
609 assertQuery(t, m, 10, 2, nil, "")
610 assertQuery(t, m, 0, 0, "hi", "(a -> b)[0].label")
611 assertQuery(t, m, 0, 0, "bye", "layers.hi.scenarios.b.(a -> b)[0].label")
612 },
613 },
614 {
615 name: "override/4",
616 run: func(t testing.TB) {
617 m, err := compile(t, `
618 (*** -> ***)[*].label: hi
619
620 a -> b: {
621 label: bye
622 }
623 `)
624 assert.Success(t, err)
625 assertQuery(t, m, 3, 1, nil, "")
626 assertQuery(t, m, 0, 0, "bye", "(a -> b)[0].label")
627 },
628 },
629 {
630 name: "override/5",
631 run: func(t testing.TB) {
632 m, err := compile(t, `
633 (*** -> ***)[*].label: hi
634
635 # This is "hey" right now but should be "hi"?
636 a -> b
637
638 (*** -> ***)[*].label: hey
639 `)
640 assert.Success(t, err)
641 assertQuery(t, m, 3, 1, nil, "")
642 assertQuery(t, m, 0, 0, "hey", "(a -> b)[0].label")
643 },
644 },
645 {
646 name: "override/6",
647 run: func(t testing.TB) {
648 m, err := compile(t, `
649 # Nulling glob doesn't work
650 a
651 *a.icon: https://icons.terrastruct.com/essentials%2F073-add.svg
652 a.icon: null
653
654 # Regular icon nulling works
655 b.icon: https://icons.terrastruct.com/essentials%2F073-add.svg
656 b.icon: null
657
658 # Shape nulling works
659 *.shape: circle
660 a.shape: null
661 b.shape: null
662 `)
663 assert.Success(t, err)
664 assertQuery(t, m, 2, 0, nil, "")
665 assertQuery(t, m, 0, 0, nil, "a")
666 assertQuery(t, m, 0, 0, nil, "b")
667 },
668 },
669 {
670 name: "override/7",
671 run: func(t testing.TB) {
672 m, err := compile(t, `
673 # Nulling glob doesn't work
674 *a.icon: https://icons.terrastruct.com/essentials%2F073-add.svg
675 a.icon: null
676
677 # Regular icon nulling works
678 b.icon: https://icons.terrastruct.com/essentials%2F073-add.svg
679 b.icon: null
680
681 # Shape nulling works
682 *.shape: circle
683 a.shape: null
684 b.shape: null
685 `)
686 assert.Success(t, err)
687 assertQuery(t, m, 2, 0, nil, "")
688 assertQuery(t, m, 0, 0, nil, "a")
689 assertQuery(t, m, 0, 0, nil, "b")
690 },
691 },
692 {
693 name: "table-class-exception",
694 run: func(t testing.TB) {
695 m, err := compile(t, `
696 ***: {
697 c: d
698 }
699
700 ***: {
701 style.fill: red
702 }
703
704 table: {
705 shape: sql_table
706 a: b
707 }
708
709 class: {
710 shape: class
711 a: b
712 }
713 `)
714 assert.Success(t, err)
715 assertQuery(t, m, 13, 0, nil, "")
716 },
717 },
718 {
719 name: "prevent-chain-recursion",
720 run: func(t testing.TB) {
721 m, err := compile(t, `
722 ***: {
723 c: d
724 }
725
726 ***: {
727 style.fill: red
728 }
729
730 one
731 two
732 `)
733 assert.Success(t, err)
734 assertQuery(t, m, 12, 0, nil, "")
735 },
736 },
737 {
738 name: "import-glob/1",
739 run: func(t testing.TB) {
740 m, err := compileFS(t, "index.d2", map[string]string{
741 "index.d2": "before; ...@globs.d2; after",
742 "globs.d2": `*: jingle
743 **: true
744 ***: meow`,
745 })
746 assert.Success(t, err)
747
748 assertQuery(t, m, 2, 0, nil, "")
749 assertQuery(t, m, 0, 0, "meow", "before")
750 assertQuery(t, m, 0, 0, "meow", "after")
751 },
752 },
753 {
754 name: "import-glob/2",
755 run: func(t testing.TB) {
756 m, err := compileFS(t, "index.d2", map[string]string{
757 "index.d2": `...@rules.d2
758 hi
759 `,
760 "rules.d2": `***.style.fill: red
761 ***: meow
762 x`,
763 })
764 assert.Success(t, err)
765
766 assertQuery(t, m, 6, 0, nil, "")
767 assertQuery(t, m, 2, 0, "meow", "hi")
768 assertQuery(t, m, 2, 0, "meow", "x")
769 assertQuery(t, m, 0, 0, "red", "hi.style.fill")
770 assertQuery(t, m, 0, 0, "red", "x.style.fill")
771 },
772 },
773 }
774
775 runa(t, tca)
776 }
777
View as plain text