cuelang.org/go@v0.13.0/cue/decode_test.go (about) 1 // Copyright 2021 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_test 16 17 import ( 18 "fmt" 19 "math/big" 20 "reflect" 21 "testing" 22 "time" 23 24 "cuelang.org/go/cue" 25 "cuelang.org/go/internal/cuetdtest" 26 "github.com/go-quicktest/qt" 27 "github.com/google/go-cmp/cmp" 28 ) 29 30 func TestDecode(t *testing.T) { 31 type Nested struct { 32 P *int `json:"P"` 33 } 34 type fields struct { 35 A int `json:"A"` 36 B int `json:"B"` 37 C int `json:"C"` 38 M map[string]interface{} 39 *Nested 40 } 41 one := 1 42 intList := func(ints ...int) *[]int { 43 ints = append([]int{}, ints...) 44 return &ints 45 } 46 testCases := []struct { 47 value string 48 dst interface{} 49 want interface{} 50 err string 51 }{{ 52 // clear pointer 53 value: `null`, 54 dst: &[]int{1}, 55 want: []int(nil), 56 }, { 57 58 value: `1`, 59 err: "cannot decode into unsettable value", 60 }, { 61 dst: new(interface{}), 62 value: `_|_`, 63 err: "explicit error (_|_ literal) in source", 64 }, { 65 // clear pointer 66 value: `null`, 67 dst: &[]int{1}, 68 want: []int(nil), 69 }, { 70 // clear pointer 71 value: `[null]`, 72 dst: &[]*int{&one}, 73 want: []*int{nil}, 74 }, { 75 value: `true`, 76 dst: new(bool), 77 want: true, 78 }, { 79 value: `false`, 80 dst: new(bool), 81 want: false, 82 }, { 83 value: `bool`, 84 dst: new(bool), 85 err: "cannot convert non-concrete value bool", 86 }, { 87 value: `_`, 88 dst: new([]int), 89 want: []int(nil), 90 }, { 91 value: `"str"`, 92 dst: new(string), 93 want: "str", 94 }, { 95 value: `"str"`, 96 dst: new(int), 97 err: "cannot use value \"str\" (type string) as int", 98 }, { 99 value: `'bytes'`, 100 dst: new([]byte), 101 want: []byte("bytes"), 102 }, { 103 value: `'bytes'`, 104 dst: &[3]byte{}, 105 want: [3]byte{0x62, 0x79, 0x74}, 106 }, { 107 value: `1`, 108 dst: new(float32), 109 want: float32(1), 110 }, { 111 value: `500`, 112 dst: new(uint8), 113 err: "integer 500 overflows uint8", 114 }, { 115 value: `501`, 116 dst: new(int8), 117 err: "integer 501 overflows int8", 118 }, { 119 value: `{}`, 120 dst: &fields{}, 121 want: fields{}, 122 }, { 123 value: `{A:1,b:2,c:3}`, 124 dst: &fields{}, 125 want: fields{A: 1, B: 2, C: 3}, 126 }, { 127 // allocate map 128 value: `{a:1,m:{a: 3}}`, 129 dst: &fields{}, 130 want: fields{A: 1, 131 M: map[string]interface{}{"a": int64(3)}}, 132 }, { 133 // indirect int 134 value: `{p: 1}`, 135 dst: &fields{}, 136 want: fields{Nested: &Nested{P: &one}}, 137 }, { 138 value: `{for k, v in y if v > 1 {"\(k)": v} } 139 y: {a:1,b:2,c:3}`, 140 dst: &fields{}, 141 want: fields{B: 2, C: 3}, 142 }, { 143 value: `{a:1,b:2,c:int}`, 144 dst: new(fields), 145 err: "c: cannot convert non-concrete value int", 146 }, { 147 value: `[]`, 148 dst: intList(), 149 want: *intList(), 150 }, { 151 value: `[1,2,3]`, 152 dst: intList(), 153 want: *intList(1, 2, 3), 154 }, { 155 // shorten list 156 value: `[1,2,3]`, 157 dst: intList(1, 2, 3, 4), 158 want: *intList(1, 2, 3), 159 }, { 160 // shorter array 161 value: `[1,2,3]`, 162 dst: &[2]int{}, 163 want: [2]int{1, 2}, 164 }, { 165 // longer array 166 value: `[1,2,3]`, 167 dst: &[4]int{}, 168 want: [4]int{1, 2, 3, 0}, 169 }, { 170 value: `[for x in #y if x > 1 { x }] 171 #y: [1,2,3]`, 172 dst: intList(), 173 want: *intList(2, 3), 174 }, { 175 value: `[int]`, 176 dst: intList(), 177 err: "0: cannot convert non-concrete value int", 178 }, { 179 value: `{a: 1, b: 2, c: 3}`, 180 dst: &map[string]int{}, 181 want: map[string]int{"a": 1, "b": 2, "c": 3}, 182 }, { 183 value: `{"1": 1, "-2": 2, "3": 3}`, 184 dst: &map[int]int{}, 185 want: map[int]int{1: 1, -2: 2, 3: 3}, 186 }, { 187 value: `{"1": 1, "2": 2, "3": 3}`, 188 dst: &map[uint]int{}, 189 want: map[uint]int{1: 1, 2: 2, 3: 3}, 190 }, { 191 value: `{a: 1, b: 2, c: true, d: e: 2}`, 192 dst: &map[string]interface{}{}, 193 want: map[string]interface{}{ 194 "a": int64(1), "b": int64(2), "c": true, 195 "d": map[string]interface{}{"e": int64(2)}}, 196 }, { 197 value: `{a: b: *2 | int}`, 198 dst: &map[string]interface{}{}, 199 want: map[string]interface{}{"a": map[string]interface{}{"b": int64(2)}}, 200 }, { 201 value: `{a: 1, b: 2, c: true}`, 202 dst: &map[string]int{}, 203 err: "c: cannot use value true (type bool) as int", 204 }, { 205 value: `{"300": 3}`, 206 dst: &map[int8]int{}, 207 err: "key integer 300 overflows int8", 208 }, { 209 value: `{"300": 3}`, 210 dst: &map[uint8]int{}, 211 err: "key integer 300 overflows uint8", 212 }, { 213 // Issue #1401 214 value: `a: b: _ | *[0, ...]`, 215 dst: &map[string]interface{}{}, 216 want: map[string]interface{}{ 217 "a": map[string]interface{}{ 218 "b": []interface{}{int64(0)}, 219 }, 220 }, 221 }, { 222 // Issue #1466 223 value: `{"x": "1s"} 224 `, 225 dst: &S{}, 226 want: S{X: Duration{D: 1000000000}}, 227 }, { 228 // Issue #1466 229 value: `{"x": '1s'} 230 `, 231 dst: &S{}, 232 want: S{X: Duration{D: 1000000000}}, 233 }, { 234 // Issue #1466 235 value: `{"x": 1} 236 `, 237 dst: &S{}, 238 err: "Decode: x: cannot use value 1 (type int) as (string|bytes)", 239 }, { 240 value: `[]`, 241 dst: new(interface{}), 242 want: []interface{}{}, 243 }, { 244 // large integer which doesn't fit into an int32 or int on 32-bit platforms 245 value: `8000000000`, 246 dst: new(interface{}), 247 want: int64(8000000000), 248 }, { 249 // same as the above, but negative 250 value: `-8000000000`, 251 dst: new(interface{}), 252 want: int64(-8000000000), 253 }, { 254 // even larger integer which doesn't fit into an int64 255 value: `9500000000000000000`, 256 dst: new(interface{}), 257 want: bigInt("9500000000000000000"), 258 }, { 259 // same as the above, but negative 260 value: `-9500000000000000000`, 261 dst: new(interface{}), 262 want: bigInt("-9500000000000000000"), 263 }, { 264 // large float which doesn't fit into a float32 265 value: `1.797693134e+308`, 266 dst: new(interface{}), 267 want: float64(1.797693134e+308), 268 }, { 269 // even larger float which doesn't fit into a float64 270 // TODO(mvdan): this should work via *big.Float, just like we do with *big.Int above. 271 value: `1.99769313499e+508`, 272 dst: new(interface{}), 273 err: "value was rounded up", 274 }, { 275 // same as the above, but negative 276 // TODO(mvdan): this should work via *big.Float, just like we do with *big.Int above. 277 value: `-1.99769313499e+508`, 278 dst: new(interface{}), 279 err: "value was rounded down", 280 }} 281 for _, tc := range testCases { 282 cuetdtest.FullMatrix.Run(t, tc.value, func(t *testing.T, m *cuetdtest.M) { 283 err := getValue(m, tc.value).Decode(tc.dst) 284 checkFatal(t, err, tc.err, "init") 285 286 got := reflect.ValueOf(tc.dst).Elem().Interface() 287 if diff := cmp.Diff(got, tc.want, cmp.Comparer(func(a, b *big.Int) bool { 288 return a.Cmp(b) == 0 289 })); diff != "" { 290 t.Error(diff) 291 t.Errorf("\n%#v\n%#v", got, tc.want) 292 } 293 }) 294 } 295 } 296 297 func bigInt(s string) *big.Int { 298 n, _ := big.NewInt(0).SetString(s, 10) 299 return n 300 } 301 302 func TestDecodeIntoCUEValue(t *testing.T) { 303 cuetdtest.FullMatrix.Do(t, func(t *testing.T, m *cuetdtest.M) { 304 // We should be able to decode into a CUE value so we can 305 // decode partially incomplete values into Go. 306 // This test doesn't fit within the table used by TestDecode 307 // because cue values aren't easily comparable with cmp.Diff. 308 var st struct { 309 X cue.Value `json:"x"` 310 } 311 err := getValue(m, `x: string`).Decode(&st) 312 qt.Assert(t, qt.IsNil(err)) 313 qt.Assert(t, qt.Equals(fmt.Sprint(st.X), "string")) 314 315 // Check we can decode into a top level value. 316 var v cue.Value 317 err = getValue(m, `int`).Decode(&v) 318 qt.Assert(t, qt.IsNil(err)) 319 qt.Assert(t, qt.Equals(fmt.Sprint(v), "int")) 320 }) 321 } 322 323 type Duration struct { 324 D time.Duration 325 } 326 type S struct { 327 X Duration `json:"x"` 328 } 329 330 func (d *Duration) UnmarshalText(data []byte) error { 331 v, err := time.ParseDuration(string(data)) 332 if err != nil { 333 return err 334 } 335 d.D = v 336 return nil 337 } 338 339 func (d *Duration) MarshalText() ([]byte, error) { 340 return []byte(d.D.String()), nil 341 }