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  }