blob: c35c9bb395404f5f0f6a510f3ebee018f716dfc5 [file] [log] [blame]
// 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)
}
})
}
}