// 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.Elem().(*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) } }) } }) } }