...

Source file src/cuelang.org/go/internal/core/export/export_test.go

Documentation: cuelang.org/go/internal/core/export

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

View as plain text