cuelang.org/go@v0.10.1/internal/core/export/export_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 export_test
    16  
    17  import (
    18  	"testing"
    19  
    20  	"golang.org/x/tools/txtar"
    21  
    22  	"cuelang.org/go/cue"
    23  	"cuelang.org/go/cue/ast"
    24  	"cuelang.org/go/cue/cuecontext"
    25  	"cuelang.org/go/cue/errors"
    26  	"cuelang.org/go/cue/format"
    27  	"cuelang.org/go/cue/parser"
    28  	"cuelang.org/go/encoding/gocode/gocodec"
    29  	"cuelang.org/go/internal/astinternal"
    30  	"cuelang.org/go/internal/core/adt"
    31  	"cuelang.org/go/internal/core/compile"
    32  	"cuelang.org/go/internal/core/convert"
    33  	"cuelang.org/go/internal/core/eval"
    34  	"cuelang.org/go/internal/core/export"
    35  	"cuelang.org/go/internal/core/runtime"
    36  	"cuelang.org/go/internal/cuetdtest"
    37  	"cuelang.org/go/internal/cuetxtar"
    38  	"cuelang.org/go/internal/value"
    39  )
    40  
    41  func TestDefinition(t *testing.T) {
    42  	test := cuetxtar.TxTarTest{
    43  		Root:   "./testdata/main",
    44  		Name:   "definition",
    45  		Matrix: cuetdtest.FullMatrix,
    46  	}
    47  
    48  	test.Run(t, func(t *cuetxtar.Test) {
    49  		r := t.Runtime()
    50  		a := t.Instance()
    51  
    52  		v, errs := compile.Files(nil, r, "", a.Files...)
    53  		if errs != nil {
    54  			t.Fatal(errs)
    55  		}
    56  		v.Finalize(eval.NewContext(r, v))
    57  
    58  		// TODO: do we need to evaluate v? In principle not necessary.
    59  		// v.Finalize(eval.NewContext(r, v))
    60  
    61  		file, errs := export.Def(r, "", v)
    62  		errors.Print(t, errs, nil)
    63  		_, _ = t.Write(formatNode(t.T, file))
    64  	})
    65  }
    66  
    67  func formatNode(t *testing.T, n ast.Node) []byte {
    68  	t.Helper()
    69  
    70  	b, err := format.Node(n)
    71  	if err != nil {
    72  		t.Fatal(err)
    73  	}
    74  	return b
    75  }
    76  
    77  // TestGenerated tests conversions of generated Go structs, which may be
    78  // different from parsed or evaluated CUE, such as having Vertex values.
    79  func TestGenerated(t *testing.T) {
    80  	cuetdtest.FullMatrix.Do(t, func(t *cuetdtest.M) {
    81  		ctx := t.Context()
    82  
    83  		testCases := []struct {
    84  			in  func(ctx *adt.OpContext) (adt.Expr, error)
    85  			out string
    86  			p   *export.Profile
    87  		}{{
    88  			in: func(ctx *adt.OpContext) (adt.Expr, error) {
    89  				in := &C{
    90  					Terminals: []*A{{Name: "Name", Description: "Desc"}},
    91  				}
    92  				return convert.GoValueToValue(ctx, in, false), nil
    93  			},
    94  			out: `Terminals: [{Name: "Name", Description: "Desc"}]`,
    95  		}, {
    96  			in: func(ctx *adt.OpContext) (adt.Expr, error) {
    97  				in := &C{
    98  					Terminals: []*A{{Name: "Name", Description: "Desc"}},
    99  				}
   100  				return convert.GoTypeToExpr(ctx, in)
   101  			},
   102  			out: `*null|{Terminals?: *null|[...*null|{Name: string, Description: string}]}`,
   103  		}, {
   104  			in: func(ctx *adt.OpContext) (adt.Expr, error) {
   105  				in := []*A{{Name: "Name", Description: "Desc"}}
   106  				return convert.GoValueToValue(ctx, in, false), nil
   107  			},
   108  			out: `[{Name: "Name", Description: "Desc"}]`,
   109  		}, {
   110  			in: func(ctx *adt.OpContext) (adt.Expr, error) {
   111  				in := []*A{{Name: "Name", Description: "Desc"}}
   112  				return convert.GoTypeToExpr(ctx, in)
   113  			},
   114  			out: `*null|[...*null|{Name: string, Description: string}]`,
   115  		}, {
   116  			in: func(ctx *adt.OpContext) (adt.Expr, error) {
   117  				expr, err := parser.ParseExpr("test", `{
   118  				x: Guide.#Terminal
   119  				Guide: {}
   120  			}`)
   121  				if err != nil {
   122  					return nil, err
   123  				}
   124  				c, err := compile.Expr(nil, ctx, "_", expr)
   125  				if err != nil {
   126  					return nil, err
   127  				}
   128  				root := &adt.Vertex{}
   129  				root.AddConjunct(c)
   130  				root.Finalize(ctx)
   131  
   132  				// Simulate Value.Unify of Lookup("x") and Lookup("Guide").
   133  				n := &adt.Vertex{}
   134  				n.AddConjunct(adt.MakeRootConjunct(nil, root.Arcs[0]))
   135  				n.AddConjunct(adt.MakeRootConjunct(nil, root.Arcs[1]))
   136  				n.Finalize(ctx)
   137  
   138  				return n, nil
   139  			},
   140  			out: `<[l2// x: undefined field: #Terminal] _|_>`,
   141  			p:   export.Final,
   142  		}, {
   143  			in: func(r *adt.OpContext) (adt.Expr, error) {
   144  				v := ctx.CompileString(`
   145  				#Provider: {
   146  					ID: string
   147  					notConcrete: bool
   148  					a: int
   149  					b: a + 1
   150  				}`)
   151  
   152  				spec := v.LookupPath(cue.ParsePath("#Provider"))
   153  				spec2 := spec.FillPath(cue.ParsePath("ID"), "12345")
   154  				root := v.FillPath(cue.ParsePath("providers.foo"), spec2)
   155  				_, n := value.ToInternal(root)
   156  
   157  				return n, nil
   158  			},
   159  			out: `#Provider: {ID: string, notConcrete: bool, a: int, b: a+1}, providers: {foo: {ID: "12345", notConcrete: bool, a: int, b: a+1}}`,
   160  			p:   export.All,
   161  		}, {
   162  			// Issue #882
   163  			in: func(r *adt.OpContext) (adt.Expr, error) {
   164  				valA := ctx.CompileString(`
   165  				#One: { version: string }
   166  			`)
   167  
   168  				valB := ctx.CompileString(`
   169  				#One: _
   170  				ones: {[string]: #One}
   171  			`)
   172  				v := valB.Unify(valA)
   173  				_, n := value.ToInternal(v)
   174  				return n, nil
   175  			},
   176  			out: `#One: {version: string}, ones: {[string]: #One}`,
   177  			p:   export.All,
   178  		}, {
   179  			// Indicate closedness in an element that is closed and misses parent
   180  			// context.
   181  			// Issue #882
   182  			in: func(r *adt.OpContext) (adt.Expr, error) {
   183  				v := ctx.CompileString(`
   184  					#A: b: c: string
   185  				`)
   186  				v = v.LookupPath(cue.ParsePath("#A.b"))
   187  
   188  				_, n := value.ToInternal(v)
   189  				return n, nil
   190  			},
   191  			out: `_#def, _#def: {c: string}`,
   192  			p:   export.All,
   193  		}, {
   194  			// Don't wrap in def if the if the value is an embedded scalar.
   195  			// Issue #977
   196  			in: func(r *adt.OpContext) (adt.Expr, error) {
   197  				v := ctx.CompileString(`
   198  					#A: { "foo", #enum: 2 }
   199  				`)
   200  				v = v.LookupPath(cue.ParsePath("#A"))
   201  
   202  				_, n := value.ToInternal(v)
   203  				return n, nil
   204  			},
   205  			out: `"foo", #enum: 2`,
   206  			p:   export.All,
   207  		}, {
   208  			// Issue #1131
   209  			in: func(r *adt.OpContext) (adt.Expr, error) {
   210  				m := make(map[string]interface{})
   211  				v := ctx.Encode(m)
   212  				_, x := value.ToInternal(v)
   213  				return x, nil
   214  			},
   215  			out: ``, // empty file
   216  		}, {
   217  			in: func(r *adt.OpContext) (adt.Expr, error) {
   218  				v := &adt.Vertex{}
   219  				v.SetValue(r, &adt.StructMarker{})
   220  				return v, nil
   221  			},
   222  			out: ``, // empty file
   223  		}}
   224  		for _, tc := range testCases {
   225  			t.Run("", func(t *testing.T) {
   226  				ctx := adt.NewContext((*runtime.Runtime)(ctx), &adt.Vertex{})
   227  				v, err := tc.in(ctx)
   228  				if err != nil {
   229  					t.Fatal("failed test case: ", err)
   230  				}
   231  
   232  				p := tc.p
   233  				if p == nil {
   234  					p = export.Simplified
   235  				}
   236  
   237  				var n ast.Node
   238  				switch x := v.(type) {
   239  				case *adt.Vertex:
   240  					n, err = p.Def(ctx, "", x)
   241  				default:
   242  					n, err = p.Expr(ctx, "", v)
   243  				}
   244  				if err != nil {
   245  					t.Fatal("failed export: ", err)
   246  				}
   247  				got := astinternal.DebugStr(n)
   248  				if got != tc.out {
   249  					t.Errorf("got:  %s\nwant: %s", got, tc.out)
   250  				}
   251  			})
   252  		}
   253  	})
   254  }
   255  
   256  type A struct {
   257  	Name        string
   258  	Description string
   259  }
   260  
   261  type B struct {
   262  	Image string
   263  }
   264  
   265  type C struct {
   266  	Terminals []*A
   267  }
   268  
   269  // For debugging purposes. Do not delete.
   270  func TestX(t *testing.T) {
   271  	t.Skip()
   272  
   273  	in := `
   274  -- in.cue --
   275  	`
   276  
   277  	archive := txtar.Parse([]byte(in))
   278  	a := cuetxtar.Load(archive, t.TempDir())
   279  	if err := a[0].Err; err != nil {
   280  		t.Fatal(err)
   281  	}
   282  
   283  	// x := a[0].Files[0]
   284  	// astutil.Sanitize(x)
   285  
   286  	ctx := cuecontext.New()
   287  	r := (*runtime.Runtime)(ctx)
   288  	v, errs := compile.Files(nil, r, "", a[0].Files...)
   289  	if errs != nil {
   290  		t.Fatal(errs)
   291  	}
   292  	v.Finalize(eval.NewContext(r, v))
   293  
   294  	file, errs := export.Def(r, "main", v)
   295  	if errs != nil {
   296  		t.Fatal(errs)
   297  	}
   298  
   299  	t.Error(string(formatNode(t, file)))
   300  }
   301  
   302  func TestFromGo(t *testing.T) {
   303  	type Struct struct {
   304  		A string
   305  		B string
   306  	}
   307  
   308  	m := make(map[string]Struct)
   309  	m["hello"] = Struct{
   310  		A: "a",
   311  		B: "b",
   312  	}
   313  	ctx := cuecontext.New()
   314  	codec := gocodec.New(ctx, nil)
   315  	v, err := codec.Decode(m)
   316  	if err != nil {
   317  		panic(err)
   318  	}
   319  
   320  	syn, _ := format.Node(v.Syntax())
   321  	if got := string(syn); got != `{
   322  	hello: {
   323  		A: "a"
   324  		B: "b"
   325  	}
   326  }` {
   327  		t.Errorf("incorrect ordering: %s\n", got)
   328  	}
   329  }
   330  
   331  func TestFromAPI(t *testing.T) {
   332  	testCases := []struct {
   333  		expr ast.Expr
   334  		out  string
   335  	}{{
   336  		expr: ast.NewCall(ast.NewIdent("close"), ast.NewStruct()),
   337  		out:  `close({})`,
   338  	}, {
   339  		expr: ast.NewCall(ast.NewIdent("close"), ast.NewStruct(
   340  			"a", ast.NewString("foo"),
   341  		)),
   342  		out: `close({a: "foo"})`,
   343  	}, {
   344  		expr: ast.NewCall(ast.NewIdent("close"), ast.NewStruct(
   345  			ast.Embed(ast.NewStruct("a", ast.NewString("foo"))),
   346  		)),
   347  		out: `close({a: "foo"})`,
   348  	}}
   349  	// Issue #1204
   350  	for _, tc := range testCases {
   351  		cuetdtest.FullMatrix.Run(t, "", func(t *cuetdtest.M) {
   352  			ctx := t.Context()
   353  
   354  			v := ctx.BuildExpr(tc.expr)
   355  
   356  			r, x := value.ToInternal(v)
   357  			file, err := export.Def(r, "foo", x)
   358  
   359  			if err != nil {
   360  				t.Fatal(err)
   361  			}
   362  
   363  			got := astinternal.DebugStr(file)
   364  			if got != tc.out {
   365  				t.Errorf("got:  %s\nwant: %s", got, tc.out)
   366  			}
   367  
   368  		})
   369  	}
   370  }