Marcel van Lohuizen | 17157ea | 2018-12-11 10:41:10 +0100 | [diff] [blame] | 1 | // Copyright 2018 The 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 cue |
| 16 | |
| 17 | import ( |
Marcel van Lohuizen | 7b5d2fd | 2018-12-11 16:34:56 +0100 | [diff] [blame] | 18 | "fmt" |
Marcel van Lohuizen | 17157ea | 2018-12-11 10:41:10 +0100 | [diff] [blame] | 19 | "math/big" |
| 20 | "reflect" |
Marcel van Lohuizen | 7b5d2fd | 2018-12-11 16:34:56 +0100 | [diff] [blame] | 21 | "strings" |
Marcel van Lohuizen | 17157ea | 2018-12-11 10:41:10 +0100 | [diff] [blame] | 22 | "testing" |
| 23 | |
| 24 | "cuelang.org/go/cue/ast" |
| 25 | "cuelang.org/go/cue/errors" |
| 26 | ) |
| 27 | |
| 28 | func TestConvert(t *testing.T) { |
| 29 | i34 := big.NewInt(34) |
| 30 | d34 := mkBigInt(34) |
| 31 | f34 := big.NewFloat(34.0000) |
| 32 | testCases := []struct { |
| 33 | goVal interface{} |
| 34 | want string |
| 35 | }{{ |
| 36 | nil, "null", |
| 37 | }, { |
| 38 | true, "true", |
| 39 | }, { |
| 40 | false, "false", |
| 41 | }, { |
| 42 | errors.New("oh noes"), "_|_(oh noes)", |
| 43 | }, { |
| 44 | "foo", `"foo"`, |
| 45 | }, { |
| 46 | 3, "3", |
| 47 | }, { |
| 48 | uint(3), "3", |
| 49 | }, { |
| 50 | uint8(3), "3", |
| 51 | }, { |
| 52 | uint16(3), "3", |
| 53 | }, { |
| 54 | uint32(3), "3", |
| 55 | }, { |
| 56 | uint64(3), "3", |
| 57 | }, { |
| 58 | int8(-3), "-3", |
| 59 | }, { |
| 60 | int16(-3), "-3", |
| 61 | }, { |
| 62 | int32(-3), "-3", |
| 63 | }, { |
| 64 | int64(-3), "-3", |
| 65 | }, { |
| 66 | float64(3.1), "3.1", |
| 67 | }, { |
| 68 | &i34, "34", |
| 69 | }, { |
| 70 | &f34, "34", |
| 71 | }, { |
| 72 | &d34, "34", |
| 73 | }, { |
| 74 | []int{1, 2, 3, 4}, "[1,2,3,4]", |
| 75 | }, { |
| 76 | []interface{}{}, "[]", |
| 77 | }, { |
| 78 | map[string][]int{ |
| 79 | "a": []int{1}, |
| 80 | "b": []int{3, 4}, |
| 81 | }, "<0>{a: [1], b: [3,4]}", |
| 82 | }, { |
| 83 | map[int]int{}, "_|_(builtin map key not a string, but unsupported type int)", |
| 84 | }, { |
| 85 | map[int]int{1: 2}, "_|_(builtin map key not a string, but unsupported type int)", |
| 86 | }, { |
| 87 | struct { |
| 88 | a int |
| 89 | b int |
| 90 | }{3, 4}, |
| 91 | "<0>{}", |
| 92 | }, { |
| 93 | struct { |
| 94 | A int |
| 95 | B int |
| 96 | }{3, 4}, |
| 97 | "<0>{A: 3, B: 4}", |
| 98 | }, { |
| 99 | struct { |
| 100 | A int `json:"a"` |
| 101 | B int `cue:"b"` |
| 102 | }{3, 4}, |
| 103 | "<0>{a: 3, b: 4}", |
| 104 | }, { |
| 105 | struct { |
| 106 | A int `json:",bb" cue:"" protobuf:"aa"` |
| 107 | B int `json:"cc" cue:"bb" protobuf:"aa"` |
| 108 | }{3, 4}, |
| 109 | "<0>{aa: 3, bb: 4}", |
| 110 | }, { |
| 111 | &struct{ A int }{3}, "<0>{A: 3}", |
| 112 | }, { |
| 113 | (*struct{ A int })(nil), "null", |
| 114 | }, { |
| 115 | reflect.ValueOf(3), "3", |
| 116 | }} |
| 117 | inst := getInstance(t, "foo") |
| 118 | b := ast.NewIdent("dummy") |
| 119 | for _, tc := range testCases { |
| 120 | ctx := inst.newContext() |
| 121 | t.Run("", func(t *testing.T) { |
| 122 | v := convert(ctx, newNode(b), tc.goVal) |
| 123 | got := debugStr(ctx, v) |
| 124 | if got != tc.want { |
| 125 | t.Errorf("got %q; want %q", got, tc.want) |
| 126 | } |
| 127 | }) |
| 128 | } |
| 129 | } |
Marcel van Lohuizen | 7b5d2fd | 2018-12-11 16:34:56 +0100 | [diff] [blame] | 130 | |
| 131 | func TestBuiltins(t *testing.T) { |
| 132 | test := func(pkg, expr string) []*bimport { |
| 133 | return []*bimport{&bimport{"", |
| 134 | []string{fmt.Sprintf("import %q\n(%s)", pkg, expr)}, |
| 135 | }} |
| 136 | } |
| 137 | testExpr := func(expr string) []*bimport { |
| 138 | return []*bimport{&bimport{"", |
| 139 | []string{fmt.Sprintf("(%s)", expr)}, |
| 140 | }} |
| 141 | } |
| 142 | testCases := []struct { |
| 143 | instances []*bimport |
| 144 | emit string |
| 145 | }{{ |
| 146 | test("math", "math.Pi"), |
| 147 | `3.14159265358979323846264338327950288419716939937510582097494459`, |
| 148 | }, { |
| 149 | test("math", "math.Floor(math.Pi)"), |
| 150 | `3`, |
| 151 | }, { |
| 152 | test("math", "math.Pi(3)"), |
| 153 | `_|_(<0>.Pi:cannot call non-function 3.14159265358979323846264338327950288419716939937510582097494459 (type float))`, |
| 154 | }, { |
| 155 | test("math", "math.Floor(3, 5)"), |
| 156 | `_|_(<0>.Floor (3,5):number of arguments does not match (1 vs 2))`, |
| 157 | }, { |
| 158 | test("math", `math.Floor("foo")`), |
| 159 | `_|_(<0>.Floor ("foo"):argument 1 requires type number, found string)`, |
| 160 | }, { |
| 161 | test("encoding/hex", `hex.Encode("foo")`), |
| 162 | `"666f6f"`, |
| 163 | }, { |
| 164 | test("encoding/hex", `hex.Decode(hex.Encode("foo"))`), |
| 165 | `'foo'`, |
| 166 | }, { |
| 167 | test("encoding/hex", `hex.Decode("foo")`), |
| 168 | `_|_(<0>.Decode ("foo"):call error: encoding/hex: invalid byte: U+006F 'o')`, |
| 169 | }, { |
| 170 | test("strconv", `strconv.FormatUint(64, 16)`), |
| 171 | `"40"`, |
| 172 | }, { |
| 173 | // Find a better alternative, as this call should go. |
| 174 | test("strconv", `strconv.FormatFloat(3.02, 300, 4, 64)`), |
| 175 | `_|_(<0>.FormatFloat (3.02,300,4,64):argument 1 out of range: has 9 > 8 bits)`, |
| 176 | }, { |
| 177 | // Find a better alternative, as this call should go. |
| 178 | test("strconv", `strconv.FormatFloat(3.02, -1, 4, 64)`), |
| 179 | `_|_(<0>.FormatFloat (3.02,-1,4,64):argument 1 must be a positive integer)`, |
| 180 | }, { |
| 181 | // Find a better alternative, as this call should go. |
| 182 | test("strconv", `strconv.FormatFloat(3.02, 1.0, 4, 64)`), |
| 183 | `_|_(<0>.FormatFloat (3.02,1.0,4,64):argument 2 requires type int, found float)`, |
| 184 | }, { |
| 185 | // Panics |
| 186 | test("math", `math.Jacobi(1000, 2000)`), |
| 187 | `_|_(<0>.Jacobi (1000,2000):call error: big: invalid 2nd argument to Int.Jacobi: need odd integer but got 2000)`, |
| 188 | }, { |
| 189 | test("math", `math.Jacobi(1000, 201)`), |
| 190 | `1`, |
| 191 | }, { |
| 192 | test("math", `math.Asin(2.0e400)`), |
| 193 | `_|_(<0>.Asin (2.0e+400):invalid argument 0: cue: value was rounded up)`, |
| 194 | }, { |
| 195 | test("encoding/csv", `csv.Decode("1,2,3\n4,5,6")`), |
| 196 | `[["1","2","3"],["4","5","6"]]`, |
| 197 | }, { |
| 198 | test("strconv", `strconv.FormatBool(true)`), |
| 199 | `"true"`, |
| 200 | }, { |
| 201 | test("strings", `strings.Join(["Hello", "World!"], " ")`), |
| 202 | `"Hello World!"`, |
| 203 | }, { |
| 204 | test("strings", `strings.Join([1, 2], " ")`), |
| 205 | `_|_(<0>.Join ([1,2]," "):list element 1: not of right kind (number vs string))`, |
| 206 | }, { |
| 207 | test("math/bits", `bits.Or(0x8, 0x1)`), |
| 208 | `9`, |
| 209 | }, { |
| 210 | testExpr(`len({})`), |
| 211 | `0`, |
| 212 | }, { |
| 213 | testExpr(`len({a: 1, b: 2, <foo>: int, _c: 3})`), |
| 214 | `2`, |
| 215 | }, { |
| 216 | testExpr(`len([1, 2, 3])`), |
| 217 | `3`, |
| 218 | }, { |
| 219 | testExpr(`len("foo")`), |
| 220 | `3`, |
| 221 | }, { |
| 222 | testExpr(`len('f\x20\x20')`), |
| 223 | `3`, |
| 224 | }} |
| 225 | for _, tc := range testCases { |
| 226 | t.Run("", func(t *testing.T) { |
| 227 | insts := Build(makeInstances(tc.instances)) |
| 228 | if err := insts[0].Err; err != nil { |
| 229 | t.Fatal(err) |
| 230 | } |
| 231 | got := strings.TrimSpace(fmt.Sprintf("%s\n", insts[0].Value())) |
| 232 | if got != tc.emit { |
| 233 | t.Errorf("\n got: %s\nwant: %s", got, tc.emit) |
| 234 | } |
| 235 | }) |
| 236 | } |
| 237 | } |