cuelang.org/go@v0.13.0/internal/core/convert/go_test.go (about) 1 // Copyright 2019 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 convert_test 16 17 // TODO: generate tests from Go's json encoder. 18 19 import ( 20 "encoding" 21 "math/big" 22 "reflect" 23 "testing" 24 "time" 25 26 "github.com/cockroachdb/apd/v3" 27 "github.com/google/go-cmp/cmp" 28 29 "cuelang.org/go/cue/errors" 30 "cuelang.org/go/internal/core/adt" 31 "cuelang.org/go/internal/core/convert" 32 "cuelang.org/go/internal/core/debug" 33 "cuelang.org/go/internal/core/runtime" 34 35 _ "cuelang.org/go/pkg" 36 ) 37 38 func mkBigInt(a int64) (v apd.Decimal) { v.SetInt64(a); return } 39 40 type textMarshaller struct { 41 b string 42 } 43 44 func (t *textMarshaller) MarshalText() (b []byte, err error) { 45 return []byte(t.b), nil 46 } 47 48 var _ encoding.TextMarshaler = &textMarshaller{} 49 50 func TestConvert(t *testing.T) { 51 type key struct { 52 a int 53 } 54 type stringType string 55 i34 := big.NewInt(34) 56 d35 := mkBigInt(35) 57 n36 := mkBigInt(-36) 58 f37 := big.NewFloat(37.0000) 59 testCases := []struct { 60 goVal interface{} 61 want string 62 }{{ 63 nil, "(_){ _ }", 64 }, { 65 true, "(bool){ true }", 66 }, { 67 false, "(bool){ false }", 68 }, { 69 errors.New("oh noes"), "(_|_){\n // [eval] oh noes\n}", 70 }, { 71 "foo", `(string){ "foo" }`, 72 }, { 73 "\x80", `(string){ "�" }`, 74 }, { 75 3, "(int){ 3 }", 76 }, { 77 uint(3), "(int){ 3 }", 78 }, { 79 uint8(3), "(int){ 3 }", 80 }, { 81 uint16(3), "(int){ 3 }", 82 }, { 83 uint32(3), "(int){ 3 }", 84 }, { 85 uint64(3), "(int){ 3 }", 86 }, { 87 int8(-3), "(int){ -3 }", 88 }, { 89 int16(-3), "(int){ -3 }", 90 }, { 91 int32(-3), "(int){ -3 }", 92 }, { 93 int64(-3), "(int){ -3 }", 94 }, { 95 float64(3), "(float){ 3 }", 96 }, { 97 float64(3.1), "(float){ 3.1 }", 98 }, { 99 float32(3.1), "(float){ 3.1 }", 100 }, { 101 uintptr(3), "(int){ 3 }", 102 }, { 103 &i34, "(int){ 34 }", 104 }, { 105 &f37, "(float){ 37 }", 106 }, { 107 &d35, "(int){ 35 }", 108 }, { 109 &n36, "(int){ -36 }", 110 }, { 111 []int{1, 2, 3, 4}, `(#list){ 112 0: (int){ 1 } 113 1: (int){ 2 } 114 2: (int){ 3 } 115 3: (int){ 4 } 116 }`, 117 }, { 118 struct { 119 A int 120 B *int 121 }{3, nil}, 122 "(struct){\n A: (int){ 3 }\n}", 123 }, { 124 []interface{}{}, "(#list){\n}", 125 }, { 126 []interface{}{nil}, "(#list){\n 0: (_){ _ }\n}", 127 }, { 128 map[string]interface{}{"a": 1, "x": nil}, `(struct){ 129 a: (int){ 1 } 130 x: (_){ _ } 131 }`, 132 }, { 133 map[string][]int{ 134 "a": {1}, 135 "b": {3, 4}, 136 }, `(struct){ 137 a: (#list){ 138 0: (int){ 1 } 139 } 140 b: (#list){ 141 0: (int){ 3 } 142 1: (int){ 4 } 143 } 144 }`, 145 }, { 146 map[bool]int{}, "(_|_){\n // [eval] unsupported Go type for map key (bool)\n}", 147 }, { 148 map[struct{}]int{{}: 2}, "(_|_){\n // [eval] unsupported Go type for map key (struct {})\n}", 149 }, { 150 map[int]int{1: 2}, `(struct){ 151 "1": (int){ 2 } 152 }`, 153 }, { 154 struct { 155 a int 156 b int 157 }{3, 4}, 158 "(struct){\n}", 159 }, { 160 struct { 161 A int 162 B int `json:"-"` 163 C int `json:",omitempty"` 164 }{3, 4, 0}, 165 `(struct){ 166 A: (int){ 3 } 167 }`, 168 }, { 169 struct { 170 A int 171 B int 172 }{3, 4}, 173 `(struct){ 174 A: (int){ 3 } 175 B: (int){ 4 } 176 }`, 177 }, { 178 struct { 179 A int `json:"a"` 180 B int `yaml:"b"` 181 }{3, 4}, 182 `(struct){ 183 a: (int){ 3 } 184 b: (int){ 4 } 185 }`, 186 }, { 187 struct { 188 A int `json:"" yaml:"" protobuf:"aa"` 189 B int `yaml:"cc" json:"bb" protobuf:"aa"` 190 }{3, 4}, 191 `(struct){ 192 aa: (int){ 3 } 193 bb: (int){ 4 } 194 }`, 195 }, { 196 &struct{ A int }{3}, `(struct){ 197 A: (int){ 3 } 198 }`, 199 }, { 200 (*struct{ A int })(nil), "(_){ _ }", 201 }, { 202 reflect.ValueOf(3), "(int){ 3 }", 203 }, { 204 time.Date(2019, 4, 1, 0, 0, 0, 0, time.UTC), `(string){ "2019-04-01T00:00:00Z" }`, 205 }, { 206 func() interface{} { 207 type T struct { 208 B int 209 } 210 type S struct { 211 A string 212 T 213 } 214 return S{} 215 }(), 216 `(struct){ 217 A: (string){ "" } 218 B: (int){ 0 } 219 }`, 220 }, 221 {map[key]string{{a: 1}: "foo"}, 222 "(_|_){\n // [eval] unsupported Go type for map key (convert_test.key)\n}"}, 223 {map[*textMarshaller]string{{b: "bar"}: "foo"}, 224 "(struct){\n \"&{bar}\": (string){ \"foo\" }\n}"}, 225 {map[int]string{1: "foo"}, 226 "(struct){\n \"1\": (string){ \"foo\" }\n}"}, 227 {map[string]encoding.TextMarshaler{"foo": nil}, 228 "(struct){\n foo: (_){ _ }\n}"}, 229 {make(chan int), 230 "(_|_){\n // [eval] unsupported Go type (chan int)\n}"}, 231 {[]interface{}{func() {}}, 232 "(_|_){\n // [eval] unsupported Go type (func())\n}"}, 233 {stringType("\x80"), `(string){ "�" }`}, 234 } 235 r := runtime.New() 236 for _, tc := range testCases { 237 ctx := adt.NewContext(r, &adt.Vertex{}) 238 t.Run("", func(t *testing.T) { 239 v := convert.GoValueToValue(ctx, tc.goVal, true) 240 n, ok := v.(*adt.Vertex) 241 if !ok { 242 n = &adt.Vertex{BaseValue: v} 243 } 244 got := debug.NodeString(ctx, n, nil) 245 if got != tc.want { 246 t.Error(cmp.Diff(got, tc.want)) 247 } 248 }) 249 } 250 } 251 252 func TestX(t *testing.T) { 253 t.Skip() 254 255 x := []string{} 256 257 r := runtime.New() 258 ctx := adt.NewContext(r, &adt.Vertex{}) 259 260 v := convert.GoValueToValue(ctx, x, false) 261 // if err != nil { 262 // t.Fatal(err) 263 // } 264 got := debug.NodeString(ctx, v, nil) 265 t.Error(got) 266 } 267 268 func TestConvertType(t *testing.T) { 269 testCases := []struct { 270 goTyp interface{} 271 want string 272 expectError bool 273 }{{ 274 goTyp: struct { 275 A int `cue:">=0&<100"` 276 B *big.Int `cue:">=0"` 277 C *big.Int 278 D big.Int 279 F *big.Float 280 }{}, 281 // TODO: indicate that B is explicitly an int only. 282 want: `{ 283 A: (((int & >=-9223372036854775808) & <=9223372036854775807) & (>=0 & <100)) 284 B: (int & >=0) 285 C?: int 286 D: int 287 F?: number 288 }`, 289 }, { 290 goTyp: &struct { 291 A int16 `cue:">=0&<100"` 292 B error `json:"b,"` 293 C string 294 D bool 295 F float64 296 L []byte 297 T time.Time 298 G func() 299 }{}, 300 want: `(*null|{ 301 A: (((int & >=-32768) & <=32767) & (>=0 & <100)) 302 b: null 303 C: string 304 D: bool 305 F: number 306 L?: (*null|bytes) 307 T: _ 308 })`, 309 }, { 310 goTyp: struct { 311 A int `cue:"<"` // invalid 312 }{}, 313 want: "_|_(invalid tag \"<\" for field \"A\": expected operand, found 'EOF' (and 1 more errors))", 314 expectError: true, 315 }, { 316 goTyp: struct { 317 A int `json:"-"` // skip 318 D *apd.Decimal 319 P ***apd.Decimal 320 I interface{ Foo() } 321 T string `cue:""` // allowed 322 h int 323 }{}, 324 want: `{ 325 D?: number 326 P?: (*null|number) 327 I?: _ 328 T: (string & _) 329 }`, 330 }, { 331 goTyp: struct { 332 A int8 `cue:"C-B"` 333 B int8 `cue:"C-A,opt"` 334 C int8 `cue:"A+B"` 335 }{}, 336 // TODO: should B be marked as optional? 337 want: `{ 338 A: (((int & >=-128) & <=127) & (〈0;C〉 - 〈0;B〉)) 339 B?: (((int & >=-128) & <=127) & (〈0;C〉 - 〈0;A〉)) 340 C: (((int & >=-128) & <=127) & (〈0;A〉 + 〈0;B〉)) 341 }`, 342 }, { 343 goTyp: []string{}, 344 want: `(*null|[ 345 ...string, 346 ])`, 347 }, { 348 goTyp: [4]string{}, 349 want: `〈import;list〉.Repeat([ 350 string, 351 ], 4)`, 352 }, { 353 goTyp: []func(){}, 354 want: "_|_(unsupported Go type (func()))", 355 expectError: true, 356 }, { 357 goTyp: map[string]struct{ A map[string]uint }{}, 358 want: `(*null|{ 359 [string]: { 360 A?: (*null|{ 361 [string]: ((int & >=0) & <=18446744073709551615) 362 }) 363 } 364 })`, 365 }, { 366 goTyp: map[float32]int{}, 367 want: `_|_(unsupported Go type for map key (float32))`, 368 expectError: true, 369 }, { 370 goTyp: map[int]map[float32]int{}, 371 want: `_|_(unsupported Go type for map key (float32))`, 372 expectError: true, 373 }, { 374 goTyp: map[int]func(){}, 375 want: `_|_(unsupported Go type (func()))`, 376 expectError: true, 377 }, { 378 goTyp: time.Now, // a function 379 want: "_|_(unsupported Go type (func() time.Time))", 380 expectError: true, 381 }, { 382 goTyp: struct { 383 Foobar string `cue:"\"foo,bar\",opt"` 384 }{}, 385 want: `{ 386 Foobar?: (string & "foo,bar") 387 }`, 388 }, { 389 goTyp: struct { 390 Foobar string `cue:"\"foo,opt,bar\""` 391 }{}, 392 want: `{ 393 Foobar: (string & "foo,opt,bar") 394 }`, 395 }} 396 397 r := runtime.New() 398 399 for _, tc := range testCases { 400 t.Run("", func(t *testing.T) { 401 ctx := adt.NewContext(r, &adt.Vertex{}) 402 v, err := convert.GoTypeToExpr(ctx, tc.goTyp) 403 got := debug.NodeString(ctx, v, nil) 404 if got != tc.want { 405 t.Errorf("\n got %q;\nwant %q", got, tc.want) 406 } 407 if tc.expectError && err == nil { 408 t.Errorf("\n expected an error but didn't get one") 409 } else if !tc.expectError && err != nil { 410 t.Errorf("\n got unexpected error: %v", err) 411 } 412 if err == nil && !tc.expectError { 413 val, _ := ctx.Evaluate(&adt.Environment{}, v) 414 if bot, ok := val.(*adt.Bottom); ok { 415 t.Errorf("\n unexpected error when evaluating result of conversion: %v", bot) 416 } 417 } 418 }) 419 } 420 }