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