cuelang.org/go@v0.10.1/internal/core/adt/eval_test.go (about) 1 // Copyright 2020 CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package adt_test 16 17 import ( 18 "flag" 19 "fmt" 20 "path/filepath" 21 "sort" 22 "strings" 23 "testing" 24 25 "golang.org/x/tools/txtar" 26 27 "cuelang.org/go/cue" 28 "cuelang.org/go/cue/ast" 29 "cuelang.org/go/cue/cuecontext" 30 "cuelang.org/go/cue/errors" 31 "cuelang.org/go/cue/stats" 32 "cuelang.org/go/cue/token" 33 "cuelang.org/go/internal" 34 "cuelang.org/go/internal/core/adt" 35 "cuelang.org/go/internal/core/debug" 36 "cuelang.org/go/internal/core/eval" 37 "cuelang.org/go/internal/core/runtime" 38 "cuelang.org/go/internal/core/validate" 39 "cuelang.org/go/internal/cuedebug" 40 "cuelang.org/go/internal/cuetxtar" 41 _ "cuelang.org/go/pkg" 42 ) 43 44 var ( 45 todo = flag.Bool("todo", false, "run tests marked with #todo-compile") 46 ) 47 48 // TestEval tests the default implementation of the evaluator. 49 func TestEval(t *testing.T) { 50 test := cuetxtar.TxTarTest{ 51 Root: "../../../cue/testdata", 52 Name: "eval", 53 Skip: alwaysSkip, 54 ToDo: needFix, 55 } 56 57 if *todo { 58 test.ToDo = nil 59 } 60 61 test.Run(t, func(tc *cuetxtar.Test) { 62 runEvalTest(tc, internal.DefaultVersion, cuedebug.Config{}) 63 }) 64 } 65 66 var alwaysSkip = map[string]string{ 67 "compile/erralias": "compile error", 68 } 69 70 var needFix = map[string]string{ 71 "DIR/NAME": "reason", 72 } 73 74 func TestEvalAlpha(t *testing.T) { 75 // TODO: remove use of externalDeps for processing. Currently, enabling 76 // this would fix some issues, but also introduce some closedness bugs. 77 // As a first step, we should ensure that the temporary hack of using 78 // externalDeps to agitate pending dependencies is replaced with a 79 // dedicated mechanism. 80 // 81 // adt.DebugDeps = true // check unmatched dependencies. 82 83 flags := cuedebug.Config{ 84 Sharing: true, 85 } 86 87 var todoAlpha = map[string]string{} 88 89 test := cuetxtar.TxTarTest{ 90 Root: "../../../cue/testdata", 91 Name: "evalalpha", 92 Fallback: "eval", // Allow eval golden files to pass these tests. 93 Skip: alwaysSkip, 94 ToDo: todoAlpha, 95 } 96 97 if *todo { 98 test.ToDo = nil 99 } 100 101 var ran, skipped, errorCount int 102 103 test.Run(t, func(t *cuetxtar.Test) { 104 if reason := skipFiles(t.Instance().Files...); reason != "" { 105 skipped++ 106 t.Skip(reason) 107 } 108 ran++ 109 110 errorCount += runEvalTest(t, internal.DevVersion, flags) 111 }) 112 113 t.Logf("todo: %d, ran: %d, skipped: %d, nodeErrors: %d", 114 len(todoAlpha), ran, skipped, errorCount) 115 } 116 117 // skipFiles returns true if the given files contain CUE that is not yet handled 118 // by the development version of the evaluator. 119 func skipFiles(a ...*ast.File) (reason string) { 120 // Skip disjunctions. 121 fn := func(n ast.Node) bool { 122 switch x := n.(type) { 123 case *ast.BinaryExpr: 124 if x.Op == token.OR { 125 // Uncomment to disable disjunction testing. 126 // NOTE: keep around until implementation of disjunctions 127 // is complete. 128 // reason = "disjunctions" 129 } 130 } 131 return true 132 } 133 for _, f := range a { 134 ast.Walk(f, fn, nil) 135 } 136 return reason 137 } 138 139 func runEvalTest(t *cuetxtar.Test, version internal.EvaluatorVersion, flags cuedebug.Config) (errorCount int) { 140 a := t.Instance() 141 r := runtime.NewWithSettings(version, flags) 142 143 v, err := r.Build(nil, a) 144 if err != nil { 145 t.WriteErrors(err) 146 return 147 } 148 149 e := eval.New(r) 150 ctx := e.NewContext(v) 151 ctx.Version = version 152 ctx.Config = flags 153 v.Finalize(ctx) 154 155 // Print discrepancies in dependencies. 156 if m := ctx.ErrorGraphs; len(m) > 0 { 157 errorCount += 1 // Could use len(m), but this seems more useful. 158 i := 0 159 keys := make([]string, len(m)) 160 for k := range m { 161 keys[i] = k 162 i++ 163 } 164 t.Errorf("unexpected node errors: %d", len(ctx.ErrorGraphs)) 165 sort.Strings(keys) 166 for _, s := range keys { 167 t.Errorf(" -- path: %s", s) 168 } 169 } 170 171 switch counts := ctx.Stats(); { 172 case version == internal.DevVersion: 173 for _, f := range t.Archive.Files { 174 if f.Name != "out/eval/stats" { 175 continue 176 } 177 c := cuecontext.New() 178 v := c.CompileBytes(f.Data) 179 var orig stats.Counts 180 v.Decode(&orig) 181 182 // TODO: do something more principled. 183 switch { 184 case orig.Disjuncts < counts.Disjuncts, 185 orig.Disjuncts > counts.Disjuncts*5 && 186 counts.Disjuncts > 20, 187 orig.Conjuncts > counts.Conjuncts*2: 188 // For now, we only care about disjuncts. 189 // TODO: add triggers once the disjunction issues have bene 190 // solved. 191 w := t.Writer("stats") 192 fmt.Fprintln(w, counts) 193 } 194 break 195 } 196 197 default: 198 w := t.Writer("stats") 199 fmt.Fprintln(w, counts) 200 } 201 202 // if n := stats.Leaks(); n > 0 { 203 // t.Skipf("%d leaks reported", n) 204 // } 205 206 if b := validate.Validate(ctx, v, &validate.Config{ 207 AllErrors: true, 208 }); b != nil { 209 fmt.Fprintln(t, "Errors:") 210 t.WriteErrors(b.Err) 211 fmt.Fprintln(t, "") 212 fmt.Fprintln(t, "Result:") 213 } 214 215 if v == nil { 216 return 217 } 218 219 t.Write(debug.AppendNode(nil, r, v, &debug.Config{Cwd: t.Dir})) 220 fmt.Fprintln(t) 221 222 return 223 } 224 225 // TestX is for debugging. Do not delete. 226 func TestX(t *testing.T) { 227 adt.DebugDeps = true 228 // adt.OpenGraphs = true 229 230 flags := cuedebug.Config{ 231 Sharing: true, // Uncomment to turn sharing off. 232 LogEval: 1, // Uncomment to turn logging off 233 } 234 235 var version internal.EvaluatorVersion 236 version = internal.DevVersion // comment to use default implementation. 237 238 in := ` 239 -- cue.mod/module.cue -- 240 module: "mod.test" 241 242 language: version: "v0.9.0" 243 244 -- in.cue -- 245 ` 246 247 if strings.HasSuffix(strings.TrimSpace(in), ".cue --") { 248 t.Skip() 249 } 250 251 a := txtar.Parse([]byte(in)) 252 instance := cuetxtar.Load(a, t.TempDir())[0] 253 if instance.Err != nil { 254 t.Fatal(instance.Err) 255 } 256 257 r := runtime.NewWithSettings(version, flags) 258 259 v, err := r.Build(nil, instance) 260 if err != nil { 261 t.Fatal(err) 262 } 263 264 e := eval.New(r) 265 ctx := e.NewContext(v) 266 ctx.Config = flags 267 v.Finalize(ctx) 268 269 out := debug.NodeString(r, v, nil) 270 if adt.OpenGraphs { 271 for p, g := range ctx.ErrorGraphs { 272 path := filepath.Join(".debug/TestX", p) 273 adt.OpenNodeGraph("TestX", path, in, out, g) 274 } 275 } 276 277 if b := validate.Validate(ctx, v, &validate.Config{ 278 AllErrors: true, 279 }); b != nil { 280 t.Log(errors.Details(b.Err, nil)) 281 } 282 283 t.Error(out) 284 285 t.Log(ctx.Stats()) 286 } 287 288 func BenchmarkUnifyAPI(b *testing.B) { 289 for i := 0; i < b.N; i++ { 290 b.StopTimer() 291 ctx := cuecontext.New() 292 v := ctx.CompileString("") 293 for j := 0; j < 500; j++ { 294 if j == 400 { 295 b.StartTimer() 296 } 297 v = v.FillPath(cue.ParsePath(fmt.Sprintf("i_%d", i)), i) 298 } 299 } 300 } 301 302 func TestIssue2293(t *testing.T) { 303 ctx := cuecontext.New() 304 c := `a: {}, a` 305 v1 := ctx.CompileString(c) 306 v2 := ctx.CompileString(c) 307 308 v1.Unify(v2) 309 }