github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/lib/golib/govalue_test.go (about) 1 package golib 2 3 import ( 4 "errors" 5 "reflect" 6 "testing" 7 8 rt "github.com/arnodel/golua/runtime" 9 ) 10 11 func Test_reflectToValue(t *testing.T) { 12 meta := rt.NewTable() 13 testTable := rt.NewTable() 14 testTable.Set(rt.StringValue("key"), rt.IntValue(123)) 15 testUdata := rt.NewUserData(1.2, meta) 16 testStruct := struct { 17 Foo int 18 Bar string 19 }{Foo: 2, Bar: "hi"} 20 tests := []struct { 21 name string 22 arg interface{} 23 want rt.Value 24 }{ 25 { 26 name: "empty", 27 arg: nil, 28 want: rt.NilValue, 29 }, 30 { 31 name: "int", 32 arg: int(8), 33 want: rt.IntValue(8), 34 }, 35 { 36 name: "int8", 37 arg: int8(-33), 38 want: rt.IntValue(-33), 39 }, 40 { 41 name: "uint", 42 arg: uint(21), 43 want: rt.IntValue(21), 44 }, 45 { 46 name: "uint16", 47 arg: uint16(777), 48 want: rt.IntValue(777), 49 }, 50 { 51 name: "float64", 52 arg: float64(1.2), 53 want: rt.FloatValue(1.2), 54 }, 55 { 56 name: "string", 57 arg: string("hello"), 58 want: rt.StringValue("hello"), 59 }, 60 { 61 name: "bool", 62 arg: true, 63 want: rt.BoolValue(true), 64 }, 65 { 66 name: "[]byte", 67 arg: []byte("bonjour"), 68 want: rt.StringValue("bonjour"), 69 }, 70 { 71 name: "lua table", 72 arg: testTable, 73 want: rt.TableValue(testTable), 74 }, 75 { 76 name: "lua userdata", 77 arg: testUdata, 78 want: rt.UserDataValue(testUdata), 79 }, 80 { 81 name: "non luable type", 82 arg: testStruct, 83 want: rt.UserDataValue(rt.NewUserData(testStruct, meta)), 84 }, 85 { 86 name: "nil slice", 87 arg: ([]int)(nil), 88 want: rt.NilValue, 89 }, 90 { 91 name: "nil pointer", 92 arg: (*int)(nil), 93 want: rt.NilValue, 94 }, 95 { 96 name: "nil interface", 97 arg: (interface{ Foo() })(nil), 98 want: rt.NilValue, 99 }, 100 } 101 for _, tt := range tests { 102 t.Run(tt.name, func(t *testing.T) { 103 if got := reflectToValue(reflect.ValueOf(tt.arg), meta); !reflect.DeepEqual(got, tt.want) { 104 t.Errorf("reflectToValue() = %v, want %v", got, tt.want) 105 } 106 }) 107 } 108 } 109 110 type tabledef map[interface{}]interface{} 111 112 func (t tabledef) table() *rt.Table { 113 tbl := rt.NewTable() 114 for k, v := range t { 115 tbl.Set(reflectToValue(reflect.ValueOf(k), nil), reflectToValue(reflect.ValueOf(v), nil)) 116 } 117 return tbl 118 } 119 120 func Test_valueToType(t *testing.T) { 121 var thread *rt.Thread 122 meta := rt.NewTable() 123 tests := []struct { 124 name string 125 v interface{} 126 want interface{} 127 wantErr bool 128 }{ 129 { 130 name: "userdata assignable", 131 v: rt.NewUserData(int(12), meta), 132 want: int(12), 133 }, 134 { 135 name: "userdata convertible", 136 v: rt.NewUserData(int8(123), meta), 137 want: int(123), 138 }, 139 { 140 name: "userdata not assignable or convertible", 141 v: rt.NewUserData(string("hello"), meta), 142 want: int(1), 143 wantErr: true, 144 }, 145 { 146 name: "pointer to non struct", 147 v: int(123), 148 want: new(int), 149 wantErr: true, 150 }, 151 { 152 name: "pointer to struct that works", 153 v: tabledef{}.table(), 154 want: &struct{}{}, 155 }, 156 { 157 name: "pointer to struct that doesn't work", 158 v: tabledef{"Foo": 2}.table(), 159 want: &struct{}{}, 160 wantErr: true, 161 }, 162 { 163 name: "struct that works", 164 v: tabledef{}.table(), 165 want: struct{}{}, 166 }, 167 { 168 name: "struct that doesn't work", 169 v: tabledef{"Foo": 2}.table(), 170 want: struct{}{}, 171 wantErr: true, 172 }, 173 { 174 name: "rt.Int to int", 175 v: int64(123), 176 want: int(123), 177 }, 178 { 179 name: "integral rt.Float to int", 180 v: float64(111), 181 want: int(111), 182 }, 183 { 184 name: "integral string to int", 185 v: "432", 186 want: int(432), 187 }, 188 { 189 name: "rt.Float to float64", 190 v: float64(1.3), 191 want: float64(1.3), 192 }, 193 { 194 name: "rt.Int to float64", 195 v: int64(-123), 196 want: float64(-123), 197 }, 198 { 199 name: "floaty string to float64", 200 v: "3.14", 201 want: float64(3.14), 202 }, 203 { 204 name: "table to float64", 205 v: rt.NewTable(), 206 want: float64(123), 207 wantErr: true, 208 }, 209 { 210 name: "nil to bool", 211 v: nil, 212 want: false, 213 }, 214 { 215 name: "int64(0) to bool", 216 v: int64(0), 217 want: true, 218 }, 219 { 220 name: "false to bool", 221 v: false, 222 want: false, 223 }, 224 { 225 name: "true to bool", 226 v: true, 227 want: true, 228 }, 229 { 230 name: "empty string to bool", 231 v: "", 232 want: true, 233 }, 234 { 235 name: "rt.String to []byte", 236 v: "foo", 237 want: []byte("foo"), 238 }, 239 { 240 name: "runtime.Value to runtime.Value", 241 v: rt.IntValue(10), 242 want: rt.IntValue(10), 243 }, 244 } 245 for _, tt := range tests { 246 t.Run(tt.name, func(t *testing.T) { 247 got, err := valueToType(thread, rt.AsValue(tt.v), reflect.TypeOf(tt.want)) 248 if (err != nil) != tt.wantErr { 249 t.Errorf("valueToType() error = %v, wantErr %v", err, tt.wantErr) 250 } 251 if tt.wantErr { 252 return 253 } 254 if !reflect.DeepEqual(got.Interface(), tt.want) { 255 t.Errorf("valueToType() = %v, want %v", got, tt.want) 256 } 257 }) 258 } 259 } 260 261 func Test_fillStruct(t *testing.T) { 262 type testStruct struct { 263 Foo int 264 } 265 thread := new(rt.Thread) 266 tests := []struct { 267 name string 268 before reflect.Value 269 after interface{} 270 v interface{} 271 wantErr bool 272 }{ 273 { 274 name: "not a table", 275 before: reflect.ValueOf(struct{}{}), 276 v: int64(12), 277 wantErr: true, 278 }, 279 { 280 name: "table with non-string field", 281 before: reflect.ValueOf(struct{}{}), 282 v: tabledef{10: 12}.table(), 283 wantErr: true, 284 }, 285 { 286 name: "success", 287 before: reflect.ValueOf(&testStruct{}).Elem(), 288 v: tabledef{"Foo": 23}.table(), 289 after: testStruct{Foo: 23}, 290 }, 291 { 292 name: "incorrect type for field", 293 before: reflect.ValueOf(&testStruct{}).Elem(), 294 v: tabledef{"Foo": "hi"}.table(), 295 wantErr: true, 296 }, 297 { 298 name: "Non-existent field", 299 before: reflect.ValueOf(&testStruct{}).Elem(), 300 v: tabledef{"Bar": 1}.table(), 301 wantErr: true, 302 }, 303 } 304 for _, tt := range tests { 305 t.Run(tt.name, func(t *testing.T) { 306 err := fillStruct(thread, tt.before, rt.AsValue(tt.v)) 307 if (err != nil) != tt.wantErr { 308 t.Errorf("fillStruct() error = %v, wantErr %v", err, tt.wantErr) 309 } 310 if !tt.wantErr && !reflect.DeepEqual(tt.before.Interface(), tt.after) { 311 t.Errorf("fillStruct() expected %s got %s", tt.after, tt.before.Interface()) 312 } 313 }) 314 } 315 } 316 317 func Test_goIndex(t *testing.T) { 318 thread := new(rt.Thread) 319 meta := rt.NewTable() 320 testErr := errors.New("hello") 321 testInt := int(12) 322 323 tests := []struct { 324 name string 325 goval interface{} 326 key interface{} 327 want interface{} 328 wantErr bool 329 doNotCheckValue bool 330 }{ 331 { 332 name: "method on struct pointer", 333 goval: testErr, 334 key: "Error", 335 doNotCheckValue: true, 336 }, 337 { 338 name: "pointer to non struct", 339 goval: &testInt, 340 key: "x", 341 wantErr: true, 342 }, 343 { 344 name: "non-string index for struct", 345 goval: struct{ Foo int }{}, 346 key: true, 347 wantErr: true, 348 }, 349 { 350 name: "index for struct not referring to a field", 351 goval: struct{ Foo int }{}, 352 key: "Bar", 353 wantErr: true, 354 }, 355 { 356 name: "index for struct referring to a field", 357 goval: struct{ Foo int }{Foo: 12}, 358 key: "Foo", 359 want: int64(12), 360 }, 361 { 362 name: "map index of incompatible type", 363 goval: map[int]int{}, 364 key: "hi", 365 wantErr: true, 366 }, 367 { 368 name: "map index of compatible type", 369 goval: map[string]int{"hi": 34}, 370 key: "hi", 371 want: int64(34), 372 }, 373 { 374 name: "non-integral slice index", 375 goval: []string{"hi", "there"}, 376 key: "bad", 377 wantErr: true, 378 }, 379 { 380 name: "integral slice index within bounds", 381 goval: []string{"hi", "there"}, 382 key: float64(1), 383 want: "there", 384 }, 385 { 386 name: "integral slice index greater than length-1", 387 goval: []string{"hi", "there"}, 388 key: float64(2), 389 wantErr: true, 390 }, 391 { 392 name: "integral slice index negative", 393 goval: []string{"hi", "there"}, 394 key: int64(-1), 395 wantErr: true, 396 }, 397 { 398 name: "unsupported type (function)", 399 goval: func() {}, 400 key: int64(1), 401 wantErr: true, 402 }, 403 } 404 for _, tt := range tests { 405 t.Run(tt.name, func(t *testing.T) { 406 got, err := goIndex(thread, rt.NewUserData(tt.goval, meta), rt.AsValue(tt.key)) 407 if (err != nil) != tt.wantErr { 408 t.Errorf("goIndex() error = %v, wantErr %v", err, tt.wantErr) 409 return 410 } 411 if !tt.wantErr && !tt.doNotCheckValue && !reflect.DeepEqual(got, rt.AsValue(tt.want)) { 412 t.Errorf("goIndex() = %v, want %v", got, tt.want) 413 } 414 }) 415 } 416 } 417 418 func Test_goSetIndex(t *testing.T) { 419 thread := new(rt.Thread) 420 meta := rt.NewTable() 421 testInt := int(12) 422 tests := []struct { 423 name string 424 goval interface{} 425 after interface{} 426 key interface{} // Will be converted with rt.AsValue 427 val interface{} // Will be converted with rt.AsValue 428 wantErr bool 429 }{ 430 { 431 name: "pointer to non struct", 432 goval: &testInt, 433 key: "key", 434 val: "val", 435 wantErr: true, 436 }, 437 { 438 name: "non string struct index", 439 goval: &struct{}{}, 440 key: true, 441 val: int64(10), 442 wantErr: true, 443 }, 444 { 445 name: "non existing struct field", 446 goval: &struct{ Foo int }{}, 447 key: "Bar", 448 val: int64(1), 449 wantErr: true, 450 }, 451 { 452 name: "struct field set to incompatible type", 453 goval: &struct{ Foo int }{}, 454 key: "Foo", 455 val: "hi", 456 wantErr: true, 457 }, 458 { 459 name: "struct field set to incompatible type", 460 goval: &struct{ Foo int }{}, 461 key: "Foo", 462 val: int64(12), 463 after: &struct{ Foo int }{Foo: 12}, 464 }, 465 { 466 name: "struct field non settable", 467 goval: struct{ Foo int }{}, 468 key: "Foo", 469 val: int64(12), 470 wantErr: true, 471 }, 472 { 473 name: "map key of incompatible type", 474 goval: map[int]string{}, 475 key: "three", 476 val: int64(444), 477 wantErr: true, 478 }, 479 { 480 name: "map value of incompatible type", 481 goval: map[int]string{}, 482 key: int64(444), 483 val: false, 484 wantErr: true, 485 }, 486 { 487 name: "map success", 488 goval: map[int]string{}, 489 key: int64(444), 490 val: "chouette", 491 after: map[int]string{444: "chouette"}, 492 }, 493 { 494 name: "non integer slice index", 495 goval: []int{3, 2, 1}, 496 key: "deux", 497 val: int64(12), 498 wantErr: true, 499 }, 500 { 501 name: "negative slice index", 502 goval: []int{3, 2, 1}, 503 key: int64(-1), 504 val: int64(12), 505 wantErr: true, 506 }, 507 { 508 name: "slice index > len-1", 509 goval: []int{3, 2, 1}, 510 key: int64(3), 511 val: int64(12), 512 wantErr: true, 513 }, 514 { 515 name: "slice value of incompatible type", 516 goval: []int{3, 2, 1}, 517 key: int64(2), 518 val: true, 519 wantErr: true, 520 }, 521 { 522 name: "successful slice", 523 goval: []int{3, 2, 1}, 524 key: int64(1), 525 val: int64(12), 526 after: []int{3, 12, 1}, 527 }, 528 { 529 name: "unsupported go type", 530 goval: false, 531 key: int64(1), 532 val: int64(2), 533 wantErr: true, 534 }, 535 } 536 for _, tt := range tests { 537 t.Run(tt.name, func(t *testing.T) { 538 u := rt.NewUserData(tt.goval, meta) 539 err := goSetIndex(thread, u, rt.AsValue(tt.key), rt.AsValue(tt.val)) 540 if (err != nil) != tt.wantErr { 541 t.Errorf("goSetIndex() error = %v, wantErr %v", err, tt.wantErr) 542 } 543 if err == nil && !reflect.DeepEqual(tt.goval, tt.after) { 544 t.Errorf("goSetIndex() got %v expected %v", tt.goval, tt.after) 545 } 546 }) 547 } 548 }