blob: 9436997a303b283aecd71f9471ee60f9635fea29 [file] [log] [blame]
// Copyright 2020 CUE Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package adt_test
import (
"testing"
"cuelang.org/go/cue/parser"
"cuelang.org/go/internal/core/adt"
"cuelang.org/go/internal/core/compile"
"cuelang.org/go/internal/core/eval"
"cuelang.org/go/internal/core/runtime"
)
// TestClosedness is a bootstrap and debugging test for developing the
// closedness algorithm. Most details of closedness is tested in the standard
// test suite.
func TestClosedness(t *testing.T) {
r := runtime.New()
ctx := eval.NewContext(r, nil)
mkStruct := func(info adt.CloseInfo, s string) *adt.StructInfo {
x, err := parser.ParseExpr("", s)
if err != nil {
t.Fatal(err)
}
expr, err := compile.Expr(nil, r, "", x)
if err != nil {
t.Fatal(err)
}
st := expr.Expr().(*adt.StructLit)
st.Init()
return &adt.StructInfo{
StructLit: st,
CloseInfo: info,
}
}
mkRef := func(s string) adt.Expr {
f := adt.MakeIdentLabel(r, s, "")
return &adt.FieldReference{Label: f}
}
type test struct {
f string
found bool
}
testCases := []struct {
desc string
n func() *adt.Vertex
tests []test
required bool
}{{
desc: "simple embedding",
// test: {
// a: 1
// c: 1
//
// def
// }
// def: {
// c: 1
// d: 1
// }
n: func() *adt.Vertex {
var (
root adt.CloseInfo
embed = root.SpawnEmbed(mkRef("dummy"))
def = embed.SpawnRef(nil, false, mkRef("def"))
)
return &adt.Vertex{
Structs: []*adt.StructInfo{
mkStruct(root, "{a: 1, c: 1}"),
mkStruct(def, "{c: 1, d: 1}"),
},
}
},
tests: []test{
{"a", true},
{"c", true},
{"d", true},
{"e", false}, // allowed, but not found
},
required: false,
}, {
desc: "closing embedding",
// test: {
// a: 1
// c: 1
//
// #def
// }
// #def: {
// c: 1
// d: 1
// }
n: func() *adt.Vertex {
var (
root adt.CloseInfo
embed = root.SpawnEmbed(mkRef("dummy"))
def = embed.SpawnRef(nil, true, mkRef("#def"))
)
return &adt.Vertex{
Structs: []*adt.StructInfo{
mkStruct(root, "{a: 1, c: 1}"),
mkStruct(def, "{c: 1, d: 1}"),
},
}
},
tests: []test{
{"a", true},
{"c", true},
{"d", true},
{"e", false},
},
required: true,
}, {
desc: "narrow down definitions in subfields",
// test: #foo
// test: {
// a: 1
// b: 1
// }
// #foo: {
// c: #bar
// c: #baz
// c: d: 1
// c: e: 1
// }
// #bar: {
// d: 1
// e: 1
// }
// #baz: {
// d: 1
// f: 1
// }
n: func() *adt.Vertex {
var (
root adt.CloseInfo
foo = root.SpawnRef(nil, true, mkRef("#foo"))
bar = foo.SpawnRef(nil, true, mkRef("#bar"))
baz = foo.SpawnRef(nil, true, mkRef("#baz"))
)
return &adt.Vertex{
Structs: []*adt.StructInfo{
mkStruct(root, "{a: 1, b:1}"),
mkStruct(foo, "{d: 1, e: 1}"),
mkStruct(bar, "{d: 1, e: 1}"),
mkStruct(baz, "{d: 1, f: 1}"),
},
}
},
tests: []test{
{"a", false},
{"b", false},
{"d", true},
{"e", false},
{"f", false},
},
required: true,
}, {
desc: "chained references",
// test: foo
// test: {
// a: 1
// b: 1
// }
// foo: bar
// bar: {
// #baz
// e: 1
// }
// #baz: {
// c: 1
// d: 1
// }
n: func() *adt.Vertex {
var (
root adt.CloseInfo
foo = root.SpawnRef(nil, false, mkRef("foo"))
bar = foo.SpawnRef(nil, false, mkRef("bar"))
baz = bar.SpawnEmbed(mkRef("#baz"))
def = baz.SpawnRef(nil, true, mkRef("#baz"))
)
return &adt.Vertex{
Structs: []*adt.StructInfo{
mkStruct(bar, "{e: 1}"),
mkStruct(def, "{c: 1, d: 1}"),
mkStruct(root, "{a: 1, c: 1}"),
},
}
},
tests: []test{
{"a", false},
{"c", true},
{"d", true},
{"e", true},
{"f", false},
},
required: true,
}, {
desc: "conjunction embedding",
// test: foo
// test: {
// a: 1
// b: 1
// }
// foo: {
// #bar & #baz
// f: 1
// }
// #bar: {
// c: 1
// d: 1
// }
// #baz: {
// d: 1
// }
// #baz: {
// e: 1
// }
n: func() *adt.Vertex {
var (
root adt.CloseInfo
foo = root.SpawnRef(nil, false, mkRef("foo"))
embed = foo.SpawnEmbed(mkRef("dummy"))
bar = embed.SpawnRef(nil, true, mkRef("#bar"))
baz = embed.SpawnRef(nil, true, mkRef("#baz"))
)
return &adt.Vertex{
Structs: []*adt.StructInfo{
mkStruct(root, "{a: 1, c: 1}"),
mkStruct(foo, "{f: 1}"),
mkStruct(bar, "{c: 1, d: 1}"),
mkStruct(baz, "{d: 1}"),
mkStruct(baz, "{e: 1}"),
},
}
},
tests: []test{
{"a", false},
{"c", false},
{"d", true},
{"e", false},
{"f", true},
{"g", false},
},
required: true,
}, {
desc: "local closing",
// test: {
// #def
// a: 1
// b: 1
// }
// test: {
// c: 1
// d: 1
// }
// #def: {
// c: 1
// e: 1
// }
n: func() *adt.Vertex {
var (
root adt.CloseInfo
// isolate local struct.
spawned = root.SpawnRef(nil, false, mkRef("dummy"))
embed = spawned.SpawnEmbed(mkRef("dummy"))
def = embed.SpawnRef(nil, true, mkRef("#def"))
)
return &adt.Vertex{
Structs: []*adt.StructInfo{
mkStruct(spawned, "{a: 1, b: 1}"),
mkStruct(root, "{c: 1, d: 1}"),
mkStruct(def, "{c: 1, e: 1}"),
},
}
},
tests: []test{
{"d", false},
{"a", true},
{"c", true},
{"e", true},
{"f", false},
},
required: true,
}, {
desc: "local closing of def",
// #test: {
// #def
// a: 1
// b: 1
// }
// #test: {
// c: 1
// d: 1
// }
// #def: {
// c: 1
// e: 1
// }
n: func() *adt.Vertex {
var (
root adt.CloseInfo
test = root.SpawnRef(nil, true, mkRef("#test"))
// isolate local struct.
spawned = test.SpawnRef(nil, false, mkRef("dummy"))
embed = spawned.SpawnEmbed(mkRef("dummy"))
def = embed.SpawnRef(nil, true, mkRef("#def"))
)
return &adt.Vertex{
Structs: []*adt.StructInfo{
mkStruct(spawned, "{a: 1, b: 1}"),
mkStruct(test, "{c: 1, d: 1}"),
mkStruct(def, "{c: 1, e: 1}"),
},
}
},
tests: []test{
{"a", true},
{"d", false},
{"c", true},
{"e", true},
{"f", false},
},
required: true,
}, {
desc: "branching",
// test: #foo
// #foo: {
// c: #bar1
// c: #bar2
// }
// #bar1: {
// d: #baz1
// d: #baz2
// }
// #bar2: {
// d: #baz3
// d: {#baz4}
// }
// #baz1: e: 1
// #baz2: e: 1
// #baz3: e: 1
// #baz4: e: 1
n: func() *adt.Vertex {
var (
root adt.CloseInfo
foo = root.SpawnRef(nil, true, mkRef("#foo"))
bar1 = foo.SpawnRef(nil, true, mkRef("#bar1"))
bar2 = foo.SpawnRef(nil, true, mkRef("#bar2"))
baz1 = bar1.SpawnRef(nil, true, mkRef("#baz1"))
baz2 = bar1.SpawnRef(nil, true, mkRef("#baz2"))
baz3 = bar2.SpawnRef(nil, true, mkRef("#baz3"))
spw3 = bar2.SpawnRef(nil, false, mkRef("spw3"))
emb2 = spw3.SpawnEmbed(mkRef("emb"))
baz4 = emb2.SpawnRef(nil, true, mkRef("#baz4"))
)
return &adt.Vertex{
Structs: []*adt.StructInfo{
mkStruct(root, "{}"),
mkStruct(foo, "{}"),
mkStruct(bar1, "{}"),
mkStruct(bar2, "{}"),
mkStruct(baz1, "{e: 1, f: 1, g: 1}"),
mkStruct(baz2, "{e: 1, f: 1, g: 1}"),
mkStruct(baz3, "{e: 1, g: 1}"),
mkStruct(baz4, "{e: 1, f: 1}"),
},
}
},
tests: []test{
{"a", false},
{"e", true},
{"f", false},
{"g", false},
},
required: true,
}}
// TODO:
// dt1: {
// #Test: {
// #SSH: !~"^ssh://"
// source: #SSH | #Test
// }
// foo: #Test & {
// source: "http://blablabla"
// }
// bar: #Test & {
// source: foo
// }
// }
//
// -----
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
n := tc.n()
for _, sub := range tc.tests {
t.Run(sub.f, func(t *testing.T) {
f := adt.MakeIdentLabel(r, sub.f, "")
ok, required := adt.Accept(ctx, n, f)
if ok != sub.found || required != tc.required {
t.Errorf("got (%v, %v); want (%v, %v)",
ok, required, sub.found, tc.required)
}
})
}
})
}
}