| // 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" |
| "math/big" |
| "reflect" |
| "strings" |
| "testing" |
| |
| "cuelang.org/go/cue/ast" |
| "cuelang.org/go/cue/errors" |
| ) |
| |
| func TestConvert(t *testing.T) { |
| i34 := big.NewInt(34) |
| d34 := mkBigInt(34) |
| f34 := big.NewFloat(34.0000) |
| testCases := []struct { |
| goVal interface{} |
| want string |
| }{{ |
| nil, "null", |
| }, { |
| true, "true", |
| }, { |
| false, "false", |
| }, { |
| errors.New("oh noes"), "_|_(oh noes)", |
| }, { |
| "foo", `"foo"`, |
| }, { |
| 3, "3", |
| }, { |
| uint(3), "3", |
| }, { |
| uint8(3), "3", |
| }, { |
| uint16(3), "3", |
| }, { |
| uint32(3), "3", |
| }, { |
| uint64(3), "3", |
| }, { |
| int8(-3), "-3", |
| }, { |
| int16(-3), "-3", |
| }, { |
| int32(-3), "-3", |
| }, { |
| int64(-3), "-3", |
| }, { |
| float64(3.1), "3.1", |
| }, { |
| &i34, "34", |
| }, { |
| &f34, "34", |
| }, { |
| &d34, "34", |
| }, { |
| []int{1, 2, 3, 4}, "[1,2,3,4]", |
| }, { |
| []interface{}{}, "[]", |
| }, { |
| map[string][]int{ |
| "a": []int{1}, |
| "b": []int{3, 4}, |
| }, "<0>{a: [1], b: [3,4]}", |
| }, { |
| map[int]int{}, "_|_(builtin map key not a string, but unsupported type int)", |
| }, { |
| map[int]int{1: 2}, "_|_(builtin map key not a string, but unsupported type int)", |
| }, { |
| struct { |
| a int |
| b int |
| }{3, 4}, |
| "<0>{}", |
| }, { |
| struct { |
| A int |
| B int |
| }{3, 4}, |
| "<0>{A: 3, B: 4}", |
| }, { |
| struct { |
| A int `json:"a"` |
| B int `cue:"b"` |
| }{3, 4}, |
| "<0>{a: 3, b: 4}", |
| }, { |
| struct { |
| A int `json:",bb" cue:"" protobuf:"aa"` |
| B int `json:"cc" cue:"bb" protobuf:"aa"` |
| }{3, 4}, |
| "<0>{aa: 3, bb: 4}", |
| }, { |
| &struct{ A int }{3}, "<0>{A: 3}", |
| }, { |
| (*struct{ A int })(nil), "null", |
| }, { |
| reflect.ValueOf(3), "3", |
| }} |
| inst := getInstance(t, "foo") |
| b := ast.NewIdent("dummy") |
| for _, tc := range testCases { |
| ctx := inst.newContext() |
| t.Run("", func(t *testing.T) { |
| v := convert(ctx, newNode(b), tc.goVal) |
| got := debugStr(ctx, v) |
| if got != tc.want { |
| t.Errorf("got %q; want %q", got, tc.want) |
| } |
| }) |
| } |
| } |
| |
| func TestBuiltins(t *testing.T) { |
| test := func(pkg, expr string) []*bimport { |
| return []*bimport{&bimport{"", |
| []string{fmt.Sprintf("import %q\n(%s)", pkg, expr)}, |
| }} |
| } |
| testExpr := func(expr string) []*bimport { |
| return []*bimport{&bimport{"", |
| []string{fmt.Sprintf("(%s)", expr)}, |
| }} |
| } |
| testCases := []struct { |
| instances []*bimport |
| emit string |
| }{{ |
| test("math", "math.Pi"), |
| `3.14159265358979323846264338327950288419716939937510582097494459`, |
| }, { |
| test("math", "math.Floor(math.Pi)"), |
| `3`, |
| }, { |
| test("math", "math.Pi(3)"), |
| `_|_(<0>.Pi:cannot call non-function 3.14159265358979323846264338327950288419716939937510582097494459 (type float))`, |
| }, { |
| test("math", "math.Floor(3, 5)"), |
| `_|_(<0>.Floor (3,5):number of arguments does not match (1 vs 2))`, |
| }, { |
| test("math", `math.Floor("foo")`), |
| `_|_(<0>.Floor ("foo"):argument 1 requires type number, found string)`, |
| }, { |
| test("encoding/hex", `hex.Encode("foo")`), |
| `"666f6f"`, |
| }, { |
| test("encoding/hex", `hex.Decode(hex.Encode("foo"))`), |
| `'foo'`, |
| }, { |
| test("encoding/hex", `hex.Decode("foo")`), |
| `_|_(<0>.Decode ("foo"):call error: encoding/hex: invalid byte: U+006F 'o')`, |
| }, { |
| test("strconv", `strconv.FormatUint(64, 16)`), |
| `"40"`, |
| }, { |
| // Find a better alternative, as this call should go. |
| test("strconv", `strconv.FormatFloat(3.02, 300, 4, 64)`), |
| `_|_(<0>.FormatFloat (3.02,300,4,64):argument 1 out of range: has 9 > 8 bits)`, |
| }, { |
| // Find a better alternative, as this call should go. |
| test("strconv", `strconv.FormatFloat(3.02, -1, 4, 64)`), |
| `_|_(<0>.FormatFloat (3.02,-1,4,64):argument 1 must be a positive integer)`, |
| }, { |
| // Find a better alternative, as this call should go. |
| test("strconv", `strconv.FormatFloat(3.02, 1.0, 4, 64)`), |
| `_|_(<0>.FormatFloat (3.02,1.0,4,64):argument 2 requires type int, found float)`, |
| }, { |
| // Panics |
| test("math", `math.Jacobi(1000, 2000)`), |
| `_|_(<0>.Jacobi (1000,2000):call error: big: invalid 2nd argument to Int.Jacobi: need odd integer but got 2000)`, |
| }, { |
| test("math", `math.Jacobi(1000, 201)`), |
| `1`, |
| }, { |
| test("math", `math.Asin(2.0e400)`), |
| `_|_(<0>.Asin (2.0e+400):invalid argument 0: cue: value was rounded up)`, |
| }, { |
| test("encoding/csv", `csv.Decode("1,2,3\n4,5,6")`), |
| `[["1","2","3"],["4","5","6"]]`, |
| }, { |
| test("strconv", `strconv.FormatBool(true)`), |
| `"true"`, |
| }, { |
| test("strings", `strings.Join(["Hello", "World!"], " ")`), |
| `"Hello World!"`, |
| }, { |
| test("strings", `strings.Join([1, 2], " ")`), |
| `_|_(<0>.Join ([1,2]," "):list element 1: not of right kind (number vs string))`, |
| }, { |
| test("math/bits", `bits.Or(0x8, 0x1)`), |
| `9`, |
| }, { |
| testExpr(`len({})`), |
| `0`, |
| }, { |
| testExpr(`len({a: 1, b: 2, <foo>: int, _c: 3})`), |
| `2`, |
| }, { |
| testExpr(`len([1, 2, 3])`), |
| `3`, |
| }, { |
| testExpr(`len("foo")`), |
| `3`, |
| }, { |
| testExpr(`len('f\x20\x20')`), |
| `3`, |
| }} |
| for _, tc := range testCases { |
| t.Run("", func(t *testing.T) { |
| insts := Build(makeInstances(tc.instances)) |
| if err := insts[0].Err; err != nil { |
| t.Fatal(err) |
| } |
| got := strings.TrimSpace(fmt.Sprintf("%s\n", insts[0].Value())) |
| if got != tc.emit { |
| t.Errorf("\n got: %s\nwant: %s", got, tc.emit) |
| } |
| }) |
| } |
| } |