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 }