d2/d2ir/pattern_test.go
2024-06-03 13:11:45 -06:00

788 lines
16 KiB
Go

package d2ir_test
import (
"testing"
"oss.terrastruct.com/util-go/assert"
)
func testCompilePatterns(t *testing.T) {
t.Parallel()
tca := []testCase{
{
name: "escaped",
run: func(t testing.TB) {
m, err := compile(t, `animal: meow
action: yes
a\*: globbed`)
assert.Success(t, err)
assertQuery(t, m, 3, 0, nil, "")
assertQuery(t, m, 0, 0, "meow", "animal")
assertQuery(t, m, 0, 0, "yes", "action")
assertQuery(t, m, 0, 0, "globbed", `a\*`)
},
},
{
name: "prefix",
run: func(t testing.TB) {
m, err := compile(t, `animal: meow
action: yes
a*: globbed`)
assert.Success(t, err)
assertQuery(t, m, 2, 0, nil, "")
assertQuery(t, m, 0, 0, "globbed", "animal")
assertQuery(t, m, 0, 0, "globbed", "action")
},
},
{
name: "case/1",
run: func(t testing.TB) {
m, err := compile(t, `animal: meow
action: yes
A*: globbed`)
assert.Success(t, err)
assertQuery(t, m, 2, 0, nil, "")
assertQuery(t, m, 0, 0, "globbed", "animal")
assertQuery(t, m, 0, 0, "globbed", "action")
},
},
{
name: "case/2",
run: func(t testing.TB) {
m, err := compile(t, `diddy kong
Donkey Kong
*kong: yes`)
assert.Success(t, err)
assertQuery(t, m, 2, 0, nil, "")
assertQuery(t, m, 0, 0, "yes", "diddy kong")
assertQuery(t, m, 0, 0, "yes", "Donkey Kong")
},
},
{
name: "suffix",
run: func(t testing.TB) {
m, err := compile(t, `animal: meow
jingle: loud
*l: globbed`)
assert.Success(t, err)
assertQuery(t, m, 2, 0, nil, "")
assertQuery(t, m, 0, 0, "globbed", "animal")
assertQuery(t, m, 0, 0, "globbed", "jingle")
},
},
{
name: "prefix-suffix",
run: func(t testing.TB) {
m, err := compile(t, `tinker: meow
thinker: yes
t*r: globbed`)
assert.Success(t, err)
assertQuery(t, m, 2, 0, nil, "")
assertQuery(t, m, 0, 0, "globbed", "tinker")
assertQuery(t, m, 0, 0, "globbed", "thinker")
},
},
{
name: "prefix-suffix/2",
run: func(t testing.TB) {
m, err := compile(t, `tinker: meow
thinker: yes
t*ink*r: globbed`)
assert.Success(t, err)
assertQuery(t, m, 2, 0, nil, "")
assertQuery(t, m, 0, 0, "globbed", "tinker")
assertQuery(t, m, 0, 0, "globbed", "thinker")
},
},
{
name: "prefix-suffix/3",
run: func(t testing.TB) {
m, err := compile(t, `tinkertinker: meow
thinkerthinker: yes
t*ink*r*t*inke*: globbed`)
assert.Success(t, err)
assertQuery(t, m, 2, 0, nil, "")
assertQuery(t, m, 0, 0, "globbed", "tinkertinker")
assertQuery(t, m, 0, 0, "globbed", "thinkerthinker")
},
},
{
name: "nested/prefix-suffix/3",
run: func(t testing.TB) {
m, err := compile(t, `animate.constant.tinkertinker: meow
astronaut.constant.thinkerthinker: yes
a*n*t*.constant.t*ink*r*t*inke*: globbed`)
assert.Success(t, err)
assertQuery(t, m, 6, 0, nil, "")
assertQuery(t, m, 0, 0, "globbed", "animate.constant.tinkertinker")
assertQuery(t, m, 0, 0, "globbed", "astronaut.constant.thinkerthinker")
},
},
{
name: "edge/1",
run: func(t testing.TB) {
m, err := compile(t, `animate
animal
an* -> an*`)
assert.Success(t, err)
assertQuery(t, m, 2, 2, nil, "")
assertQuery(t, m, 0, 0, nil, "(animate -> animal)[0]")
assertQuery(t, m, 0, 0, nil, "(animal -> animate)[0]")
},
},
{
name: "edge/2",
run: func(t testing.TB) {
m, err := compile(t, `shared.animate
shared.animal
sh*.(an* -> an*)`)
assert.Success(t, err)
assertQuery(t, m, 3, 2, nil, "")
assertQuery(t, m, 2, 2, nil, "shared")
assertQuery(t, m, 0, 0, nil, "shared.(animate -> animal)[0]")
assertQuery(t, m, 0, 0, nil, "shared.(animal -> animate)[0]")
},
},
{
name: "edge/3",
run: func(t testing.TB) {
m, err := compile(t, `shared.animate
shared.animal
sh*.an* -> sh*.an*`)
assert.Success(t, err)
assertQuery(t, m, 3, 2, nil, "")
assertQuery(t, m, 2, 2, nil, "shared")
assertQuery(t, m, 0, 0, nil, "shared.(animate -> animal)[0]")
assertQuery(t, m, 0, 0, nil, "shared.(animal -> animate)[0]")
},
},
{
name: "edge-glob-index",
run: func(t testing.TB) {
m, err := compile(t, `a -> b
a -> b
a -> b
(a -> b)[*].style.fill: red
`)
assert.Success(t, err)
assertQuery(t, m, 8, 3, nil, "")
assertQuery(t, m, 0, 0, "red", "(a -> b)[0].style.fill")
assertQuery(t, m, 0, 0, "red", "(a -> b)[1].style.fill")
assertQuery(t, m, 0, 0, "red", "(a -> b)[2].style.fill")
},
},
{
name: "glob-edge-glob-index",
run: func(t testing.TB) {
m, err := compile(t, `a -> b
a -> b
a -> b
c -> b
(* -> b)[*].style.fill: red
`)
assert.Success(t, err)
assertQuery(t, m, 11, 4, nil, "")
assertQuery(t, m, 0, 0, "red", "(a -> b)[0].style.fill")
assertQuery(t, m, 0, 0, "red", "(a -> b)[1].style.fill")
assertQuery(t, m, 0, 0, "red", "(a -> b)[2].style.fill")
assertQuery(t, m, 0, 0, "red", "(c -> b)[0].style.fill")
},
},
{
name: "edge-nexus",
run: func(t testing.TB) {
m, err := compile(t, `a
b
c
d
* -> nexus
`)
assert.Success(t, err)
assertQuery(t, m, 5, 4, nil, "")
assertQuery(t, m, 0, 0, nil, "(a -> nexus)[0]")
assertQuery(t, m, 0, 0, nil, "(b -> nexus)[0]")
assertQuery(t, m, 0, 0, nil, "(c -> nexus)[0]")
assertQuery(t, m, 0, 0, nil, "(d -> nexus)[0]")
},
},
{
name: "double-glob/1",
run: func(t testing.TB) {
m, err := compile(t, `shared.animate
shared.animal
**.style.fill: red`)
assert.Success(t, err)
assertQuery(t, m, 9, 0, nil, "")
assertQuery(t, m, 8, 0, nil, "shared")
assertQuery(t, m, 1, 0, nil, "shared.style")
assertQuery(t, m, 2, 0, nil, "shared.animate")
assertQuery(t, m, 1, 0, nil, "shared.animate.style")
assertQuery(t, m, 2, 0, nil, "shared.animal")
assertQuery(t, m, 1, 0, nil, "shared.animal.style")
},
},
{
name: "double-glob/edge-no-container",
run: func(t testing.TB) {
m, err := compile(t, `zone A: {
machine A
machine B: {
submachine A
submachine B
}
}
zone A.** -> load balancer
`)
assert.Success(t, err)
assertQuery(t, m, 6, 3, nil, "")
},
},
{
name: "reserved",
run: func(t testing.TB) {
m, err := compile(t, `vars: {
d2-config: {
layout-engine: elk
}
}
Spiderman 1
Spiderman 2
Spiderman 3
* -> *: arrow`)
assert.Success(t, err)
assertQuery(t, m, 6, 6, nil, "")
assertQuery(t, m, 0, 0, "arrow", "(* -> *)[*]")
},
},
{
name: "scenarios",
run: func(t testing.TB) {
m, err := compile(t, `
scenarios: {
meow: {
e
f
g
h
}
}
a
b
c
d
**: something
** -> **
`)
assert.Success(t, err)
assertQuery(t, m, 10, 24, nil, "")
assertQuery(t, m, 0, 0, "something", "**")
assertQuery(t, m, 0, 0, nil, "(* -> *)[*]")
},
},
{
name: "single-glob/defaults",
run: func(t testing.TB) {
m, err := compile(t, `wrapper.*: {
shape: page
}
wrapper.a
wrapper.b
wrapper.c
wrapper.d
scenarios.x: { wrapper.p }
layers.x: { wrapper.p }
`)
assert.Success(t, err)
assertQuery(t, m, 26, 0, nil, "")
assertQuery(t, m, 0, 0, "page", "wrapper.a.shape")
assertQuery(t, m, 0, 0, "page", "wrapper.b.shape")
assertQuery(t, m, 0, 0, "page", "wrapper.c.shape")
assertQuery(t, m, 0, 0, "page", "wrapper.d.shape")
assertQuery(t, m, 0, 0, "page", "scenarios.x.wrapper.p.shape")
assertQuery(t, m, 0, 0, nil, "layers.x.wrapper.p")
},
},
{
name: "edge-glob-null",
run: func(t testing.TB) {
m, err := compile(t, `a -> b
(* -> *)[*]: null
x -> y
`)
assert.Success(t, err)
// 4 fields and 0 edges
assertQuery(t, m, 4, 0, nil, "")
},
},
{
name: "double-glob/edge/1",
run: func(t testing.TB) {
m, err := compile(t, `fast: {
a
far
}
task: {
a
}
task.** -> fast
`)
assert.Success(t, err)
assertQuery(t, m, 5, 1, nil, "")
},
},
{
name: "double-glob/edge/2",
run: func(t testing.TB) {
m, err := compile(t, `a
**.b -> c
`)
assert.Success(t, err)
assertQuery(t, m, 3, 1, nil, "")
},
},
{
name: "double-glob/defaults",
run: func(t testing.TB) {
m, err := compile(t, `**: {
shape: page
}
a
b
c
d
scenarios.x: { p }
layers.x: { p }
`)
assert.Success(t, err)
assertQuery(t, m, 23, 0, nil, "")
assertQuery(t, m, 0, 0, "page", "a.shape")
assertQuery(t, m, 0, 0, "page", "b.shape")
assertQuery(t, m, 0, 0, "page", "c.shape")
assertQuery(t, m, 0, 0, "page", "d.shape")
assertQuery(t, m, 0, 0, "page", "scenarios.x.p.shape")
assertQuery(t, m, 0, 0, nil, "layers.x.p")
},
},
{
name: "triple-glob/defaults",
run: func(t testing.TB) {
m, err := compile(t, `***: {
shape: page
}
a
b
c
d
scenarios.x: { p }
layers.x: { p }
`)
assert.Success(t, err)
assertQuery(t, m, 24, 0, nil, "")
assertQuery(t, m, 0, 0, "page", "a.shape")
assertQuery(t, m, 0, 0, "page", "b.shape")
assertQuery(t, m, 0, 0, "page", "c.shape")
assertQuery(t, m, 0, 0, "page", "d.shape")
assertQuery(t, m, 0, 0, "page", "scenarios.x.p.shape")
assertQuery(t, m, 0, 0, "page", "layers.x.p.shape")
},
},
{
name: "triple-glob/edge-defaults",
run: func(t testing.TB) {
m, err := compile(t, `(*** -> ***)[*]: {
target-arrowhead.shape: diamond
}
a -> b
c -> d
scenarios.x: { p -> q }
layers.x: { j -> f }
`)
assert.Success(t, err)
assertQuery(t, m, 28, 6, nil, "")
assertQuery(t, m, 0, 0, "diamond", "(a -> b)[0].target-arrowhead.shape")
assertQuery(t, m, 0, 0, "diamond", "(c -> d)[0].target-arrowhead.shape")
assertQuery(t, m, 0, 0, "diamond", "scenarios.x.(a -> b)[0].target-arrowhead.shape")
assertQuery(t, m, 0, 0, "diamond", "scenarios.x.(c -> d)[0].target-arrowhead.shape")
assertQuery(t, m, 0, 0, "diamond", "scenarios.x.(p -> q)[0].target-arrowhead.shape")
assertQuery(t, m, 4, 1, nil, "layers.x")
assertQuery(t, m, 0, 0, "diamond", "layers.x.(j -> f)[0].target-arrowhead.shape")
},
},
{
name: "alixander-review/1",
run: func(t testing.TB) {
m, err := compile(t, `
***.style.fill: yellow
**.shape: circle
*.style.multiple: true
x: {
y
}
layers: {
next: {
a
}
}
`)
assert.Success(t, err)
assertQuery(t, m, 14, 0, nil, "")
},
},
{
name: "alixander-review/2",
run: func(t testing.TB) {
m, err := compile(t, `
a
* -> y
b
c
`)
assert.Success(t, err)
assertQuery(t, m, 4, 3, nil, "")
},
},
{
name: "alixander-review/3",
run: func(t testing.TB) {
m, err := compile(t, `
a
***.b -> c
layers: {
z: {
d
}
}
`)
assert.Success(t, err)
assertQuery(t, m, 8, 2, nil, "")
},
},
{
name: "alixander-review/4",
run: func(t testing.TB) {
m, err := compile(t, `
**.child
a
b
c
`)
assert.Success(t, err)
assertQuery(t, m, 6, 0, nil, "")
},
},
{
name: "alixander-review/5",
run: func(t testing.TB) {
m, err := compile(t, `
**.style.fill: red
scenarios: {
b: {
a -> b
}
}
`)
assert.Success(t, err)
assertQuery(t, m, 8, 1, nil, "")
assertQuery(t, m, 0, 0, "red", "scenarios.b.a.style.fill")
assertQuery(t, m, 0, 0, "red", "scenarios.b.b.style.fill")
},
},
{
name: "alixander-review/6",
run: func(t testing.TB) {
m, err := compile(t, `
(* -> *)[*].style.opacity: 0.1
x -> y: hi
x -> y
`)
assert.Success(t, err)
assertQuery(t, m, 6, 2, nil, "")
assertQuery(t, m, 0, 0, 0.1, "(x -> y)[0].style.opacity")
assertQuery(t, m, 0, 0, 0.1, "(x -> y)[1].style.opacity")
},
},
{
name: "alixander-review/7",
run: func(t testing.TB) {
m, err := compile(t, `
*: {
style.fill: red
}
**: {
style.fill: red
}
table: {
style.fill: blue
shape: sql_table
a: b
}
`)
assert.Success(t, err)
assertQuery(t, m, 7, 0, nil, "")
assertQuery(t, m, 0, 0, "blue", "table.style.fill")
},
},
{
name: "alixander-review/8",
run: func(t testing.TB) {
m, err := compile(t, `
(a -> *)[*].style.stroke: red
(* -> *)[*].style.stroke: red
b -> c
`)
assert.Success(t, err)
assertQuery(t, m, 4, 1, nil, "")
assertQuery(t, m, 0, 0, "red", "(b -> c)[0].style.stroke")
},
},
{
name: "override/1",
run: func(t testing.TB) {
m, err := compile(t, `
**.style.fill: yellow
**.style.fill: red
a
`)
assert.Success(t, err)
assertQuery(t, m, 3, 0, nil, "")
assertQuery(t, m, 0, 0, "red", "a.style.fill")
},
},
{
name: "override/2",
run: func(t testing.TB) {
m, err := compile(t, `
***.style.fill: yellow
layers: {
hi: {
**.style.fill: red
# should be red, but it's yellow right now
a
}
}
`)
assert.Success(t, err)
assertQuery(t, m, 5, 0, nil, "")
assertQuery(t, m, 0, 0, "red", "layers.hi.a.style.fill")
},
},
{
name: "override/3",
run: func(t testing.TB) {
m, err := compile(t, `
(*** -> ***)[*].label: hi
a -> b
layers: {
hi: {
(*** -> ***)[*].label: bye
scenarios: {
b: {
# This label is "hi", but it should be "bye"
a -> b
}
}
}
}
`)
assert.Success(t, err)
assertQuery(t, m, 10, 2, nil, "")
assertQuery(t, m, 0, 0, "hi", "(a -> b)[0].label")
assertQuery(t, m, 0, 0, "bye", "layers.hi.scenarios.b.(a -> b)[0].label")
},
},
{
name: "override/4",
run: func(t testing.TB) {
m, err := compile(t, `
(*** -> ***)[*].label: hi
a -> b: {
label: bye
}
`)
assert.Success(t, err)
assertQuery(t, m, 3, 1, nil, "")
assertQuery(t, m, 0, 0, "bye", "(a -> b)[0].label")
},
},
{
name: "override/5",
run: func(t testing.TB) {
m, err := compile(t, `
(*** -> ***)[*].label: hi
# This is "hey" right now but should be "hi"?
a -> b
(*** -> ***)[*].label: hey
`)
assert.Success(t, err)
assertQuery(t, m, 3, 1, nil, "")
assertQuery(t, m, 0, 0, "hey", "(a -> b)[0].label")
},
},
{
name: "override/6",
run: func(t testing.TB) {
m, err := compile(t, `
# Nulling glob doesn't work
a
*a.icon: https://icons.terrastruct.com/essentials%2F073-add.svg
a.icon: null
# Regular icon nulling works
b.icon: https://icons.terrastruct.com/essentials%2F073-add.svg
b.icon: null
# Shape nulling works
*.shape: circle
a.shape: null
b.shape: null
`)
assert.Success(t, err)
assertQuery(t, m, 2, 0, nil, "")
assertQuery(t, m, 0, 0, nil, "a")
assertQuery(t, m, 0, 0, nil, "b")
},
},
{
name: "override/7",
run: func(t testing.TB) {
m, err := compile(t, `
# Nulling glob doesn't work
*a.icon: https://icons.terrastruct.com/essentials%2F073-add.svg
a.icon: null
# Regular icon nulling works
b.icon: https://icons.terrastruct.com/essentials%2F073-add.svg
b.icon: null
# Shape nulling works
*.shape: circle
a.shape: null
b.shape: null
`)
assert.Success(t, err)
assertQuery(t, m, 2, 0, nil, "")
assertQuery(t, m, 0, 0, nil, "a")
assertQuery(t, m, 0, 0, nil, "b")
},
},
{
name: "table-class-exception",
run: func(t testing.TB) {
m, err := compile(t, `
***: {
c: d
}
***: {
style.fill: red
}
table: {
shape: sql_table
a: b
}
class: {
shape: class
a: b
}
`)
assert.Success(t, err)
assertQuery(t, m, 13, 0, nil, "")
},
},
{
name: "prevent-chain-recursion",
run: func(t testing.TB) {
m, err := compile(t, `
***: {
c: d
}
***: {
style.fill: red
}
one
two
`)
assert.Success(t, err)
assertQuery(t, m, 12, 0, nil, "")
},
},
{
name: "import-glob/1",
run: func(t testing.TB) {
m, err := compileFS(t, "index.d2", map[string]string{
"index.d2": "before; ...@globs.d2; after",
"globs.d2": `*: jingle
**: true
***: meow`,
})
assert.Success(t, err)
assertQuery(t, m, 2, 0, nil, "")
assertQuery(t, m, 0, 0, "meow", "before")
assertQuery(t, m, 0, 0, "meow", "after")
},
},
{
name: "import-glob/2",
run: func(t testing.TB) {
m, err := compileFS(t, "index.d2", map[string]string{
"index.d2": `...@rules.d2
hi
`,
"rules.d2": `***.style.fill: red
***: meow
x`,
})
assert.Success(t, err)
assertQuery(t, m, 6, 0, nil, "")
assertQuery(t, m, 2, 0, "meow", "hi")
assertQuery(t, m, 2, 0, "meow", "x")
assertQuery(t, m, 0, 0, "red", "hi.style.fill")
assertQuery(t, m, 0, 0, "red", "x.style.fill")
},
},
}
runa(t, tca)
}