| // Copyright 2018 The CUE Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package cue |
| |
| import ( |
| "fmt" |
| "strings" |
| "testing" |
| |
| "cuelang.org/go/cue/ast" |
| "cuelang.org/go/cue/build" |
| "cuelang.org/go/cue/token" |
| ) |
| |
| func TestFromExpr(t *testing.T) { |
| testCases := []struct { |
| expr ast.Expr |
| out string |
| }{{ |
| expr: ast.NewString("Hello"), |
| out: `"Hello"`, |
| }, { |
| expr: ast.NewList( |
| ast.NewString("Hello"), |
| ast.NewString("World"), |
| ), |
| out: `["Hello","World"]`, |
| }} |
| for _, tc := range testCases { |
| t.Run("", func(t *testing.T) { |
| r := &Runtime{} |
| inst, err := r.CompileExpr(tc.expr) |
| if err != nil { |
| t.Fatal(err) |
| } |
| ctx := inst.newContext() |
| if got := debugStr(ctx, inst.eval(ctx)); got != tc.out { |
| t.Errorf("\n got: %v; want %v", got, tc.out) |
| } |
| }) |
| } |
| } |
| |
| func TestBuild(t *testing.T) { |
| files := func(s ...string) []string { return s } |
| insts := func(i ...*bimport) []*bimport { return i } |
| pkg1 := &bimport{ |
| "pkg1", |
| files(` |
| package pkg1 |
| |
| Object: "World" |
| `), |
| } |
| pkg2 := &bimport{ |
| "example.com/foo/pkg2:pkg", |
| files(` |
| package pkg |
| |
| Number: 12 |
| `), |
| } |
| pkg3 := &bimport{ |
| "example.com/foo/v1:pkg3", |
| files(` |
| package pkg3 |
| |
| List: [1,2,3] |
| `), |
| } |
| |
| testCases := []struct { |
| instances []*bimport |
| emit string |
| }{{ |
| insts(&bimport{"", files(`test: "ok"`)}), |
| `{test: "ok"}`, |
| }, { |
| insts(&bimport{"", |
| files( |
| `package test |
| |
| import "math" |
| |
| "Pi: \(math.Pi)!"`)}), |
| `"Pi: 3.14159265358979323846264338327950288419716939937510582097494459!"`, |
| }, { |
| insts(&bimport{"", |
| files( |
| `package test |
| |
| import math2 "math" |
| |
| "Pi: \(math2.Pi)!"`)}), |
| `"Pi: 3.14159265358979323846264338327950288419716939937510582097494459!"`, |
| }, { |
| insts(pkg1, &bimport{"", |
| files( |
| `package test |
| |
| import "pkg1" |
| |
| "Hello \(pkg1.Object)!"`), |
| }), |
| `"Hello World!"`, |
| }, { |
| insts(pkg1, &bimport{"", |
| files( |
| `package test |
| |
| import "pkg1" |
| |
| "Hello \(pkg1.Object)!"`), |
| }), |
| `"Hello World!"`, |
| }, { |
| insts(pkg1, &bimport{"", |
| files( |
| `package test |
| |
| import pkg2 "pkg1" |
| pkg1 :: pkg2.Object |
| |
| "Hello \(pkg1)!"`), |
| }), |
| `"Hello World!"`, |
| }, { |
| insts(pkg1, &bimport{"", |
| files( |
| `package test |
| |
| import bar "pkg1" |
| |
| pkg1 Object: 3 |
| "Hello \(pkg1.Object)!"`), |
| }), |
| `imported and not used: "pkg1" as bar`, |
| }, { |
| insts(pkg2, &bimport{"", |
| files( |
| `package test |
| |
| import "example.com/foo/pkg2:pkg" |
| |
| "Hello \(pkg2.Number)!"`), |
| }), |
| `imported and not used: "example.com/foo/pkg2:pkg" (and 1 more errors)`, |
| // `file0.cue:5:14: unresolved reference pkg2`, |
| }, { |
| insts(pkg2, &bimport{"", |
| files( |
| `package test |
| |
| import "example.com/foo/pkg2:pkg" |
| |
| "Hello \(pkg.Number)!"`), |
| }), |
| `"Hello 12!"`, |
| }, { |
| insts(pkg3, &bimport{"", |
| files( |
| `package test |
| |
| import "example.com/foo/v1:pkg3" |
| |
| "Hello \(pkg3.List[1])!"`), |
| }), |
| `"Hello 2!"`, |
| }, { |
| insts(pkg3, &bimport{"", |
| files( |
| `package test |
| |
| import "example.com/foo/v1:pkg3" |
| |
| pkg3: 3 |
| |
| "Hello \(pkg3.List[1])!"`), |
| }), |
| `pkg3 redeclared as imported package name |
| previous declaration at file0.cue:5:5`, |
| }} |
| for _, tc := range testCases { |
| t.Run("", func(t *testing.T) { |
| insts := Build(makeInstances(tc.instances)) |
| var got string |
| if err := insts[0].Err; err != nil { |
| got = err.Error() |
| } else { |
| got = strings.TrimSpace(fmt.Sprintf("%s\n", insts[0].Value())) |
| } |
| if got != tc.emit { |
| t.Errorf("\n got: %s\nwant: %s", got, tc.emit) |
| } |
| }) |
| } |
| } |
| |
| type builder struct { |
| ctxt *build.Context |
| imports map[string]*bimport |
| } |
| |
| func (b *builder) load(pos token.Pos, path string) *build.Instance { |
| bi := b.imports[path] |
| if bi == nil { |
| return nil |
| } |
| return b.build(bi) |
| } |
| |
| type bimport struct { |
| path string // "" means top-level |
| files []string |
| } |
| |
| func makeInstances(insts []*bimport) (instances []*build.Instance) { |
| b := builder{ |
| ctxt: build.NewContext(), |
| imports: map[string]*bimport{}, |
| } |
| for _, bi := range insts { |
| if bi.path != "" { |
| b.imports[bi.path] = bi |
| } |
| } |
| for _, bi := range insts { |
| if bi.path == "" { |
| instances = append(instances, b.build(bi)) |
| } |
| } |
| return |
| } |
| |
| func (b *builder) build(bi *bimport) *build.Instance { |
| path := bi.path |
| if path == "" { |
| path = "dir" |
| } |
| p := b.ctxt.NewInstance(path, b.load) |
| for i, f := range bi.files { |
| _ = p.AddFile(fmt.Sprintf("file%d.cue", i), f) |
| } |
| _ = p.Complete() |
| return p |
| } |