github.com/google/grumpy@v0.0.0-20171122020858-3ec87959189c/runtime/native_test.go (about)

     1  // Copyright 2016 Google Inc. All Rights Reserved.
     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 grumpy
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"math/big"
    21  	"reflect"
    22  	"regexp"
    23  	"testing"
    24  )
    25  
    26  func TestNativeMetaclassNew(t *testing.T) {
    27  	var i int16
    28  	intType := newNativeType(reflect.TypeOf(i), IntType)
    29  	fun := wrapFuncForTest(func(f *Frame, args ...*Object) *BaseException {
    30  		newFunc, raised := GetAttr(f, intType.ToObject(), NewStr("new"), nil)
    31  		if raised != nil {
    32  			return raised
    33  		}
    34  		ret, raised := newFunc.Call(f, args, nil)
    35  		if raised != nil {
    36  			return raised
    37  		}
    38  		got, raised := ToNative(f, ret)
    39  		if raised != nil {
    40  			return raised
    41  		}
    42  		if got.Type() != reflect.TypeOf(&i) {
    43  			t.Errorf("%v.new() returned a %s, want a *int16", intType, nativeTypeName(got.Type()))
    44  		} else if p, ok := got.Interface().(*int16); !ok || p == nil || *p != 0 {
    45  			t.Errorf("%v.new() returned %v, want &int16(0)", intType, got)
    46  		}
    47  		return nil
    48  	})
    49  	cases := []invokeTestCase{
    50  		{want: None},
    51  		{args: wrapArgs("abc"), wantExc: mustCreateException(TypeErrorType, "'new' of 'nativetype' requires 1 arguments")},
    52  	}
    53  	for _, cas := range cases {
    54  		if err := runInvokeTestCase(fun, &cas); err != "" {
    55  			t.Error(err)
    56  		}
    57  	}
    58  }
    59  
    60  func TestNativeFuncCall(t *testing.T) {
    61  	cases := []struct {
    62  		fun interface{}
    63  		invokeTestCase
    64  	}{
    65  		{func() {}, invokeTestCase{want: None}},
    66  		{func() float32 { return 2.0 }, invokeTestCase{want: NewFloat(2.0).ToObject()}},
    67  		{func(s string) string { return s }, invokeTestCase{args: wrapArgs("foo"), want: NewStr("foo").ToObject()}},
    68  		{func() (int, string) { return 42, "bar" }, invokeTestCase{want: newTestTuple(42, "bar").ToObject()}},
    69  		{func(s ...string) int { return len(s) }, invokeTestCase{args: wrapArgs("foo", "bar"), want: NewInt(2).ToObject()}},
    70  		{func() {}, invokeTestCase{args: wrapArgs(3.14), wantExc: mustCreateException(TypeErrorType, "native function takes 0 arguments, (1 given)")}},
    71  		{func(int, ...string) {}, invokeTestCase{wantExc: mustCreateException(TypeErrorType, "native function takes at least 1 arguments, (0 given)")}},
    72  	}
    73  	for _, cas := range cases {
    74  		n := &native{Object{typ: nativeFuncType}, reflect.ValueOf(cas.fun)}
    75  		if err := runInvokeTestCase(n.ToObject(), &cas.invokeTestCase); err != "" {
    76  			t.Error(err)
    77  		}
    78  	}
    79  }
    80  
    81  func TestNativeFuncName(t *testing.T) {
    82  	re := regexp.MustCompile(`(\w+\.)*\w+$`)
    83  	fun := wrapFuncForTest(func(f *Frame, o *Object) (string, *BaseException) {
    84  		desc, raised := GetItem(f, nativeFuncType.Dict().ToObject(), internedName.ToObject())
    85  		if raised != nil {
    86  			return "", raised
    87  		}
    88  		get, raised := GetAttr(f, desc, NewStr("__get__"), nil)
    89  		if raised != nil {
    90  			return "", raised
    91  		}
    92  		name, raised := get.Call(f, wrapArgs(o, nativeFuncType), nil)
    93  		if raised != nil {
    94  			return "", raised
    95  		}
    96  		if raised := Assert(f, GetBool(name.isInstance(StrType)).ToObject(), nil); raised != nil {
    97  			return "", raised
    98  		}
    99  		return re.FindString(toStrUnsafe(name).Value()), nil
   100  	})
   101  	cases := []invokeTestCase{
   102  		{args: wrapArgs(TestNativeFuncName), want: NewStr("grumpy.TestNativeFuncName").ToObject()},
   103  		{args: wrapArgs(None), wantExc: mustCreateException(TypeErrorType, "'_get_name' requires a 'func' object but received a 'NoneType'")},
   104  	}
   105  	for _, cas := range cases {
   106  		if err := runInvokeTestCase(fun, &cas); err != "" {
   107  			t.Error(err)
   108  		}
   109  	}
   110  }
   111  
   112  func TestNativeFuncStrRepr(t *testing.T) {
   113  	cases := []struct {
   114  		args        Args
   115  		wantPattern string
   116  	}{
   117  		{wrapArgs(TestNativeFuncStrRepr), `<func\(\*T\) .*grumpy\.TestNativeFuncStrRepr at 0x[a-f0-9]+>`},
   118  		{wrapArgs(func() {}), `<func\(\) .*grumpy\.TestNativeFuncStrRepr\.\w+ at 0x[a-f0-9]+>`},
   119  		{wrapArgs(Repr), `<func\(\*Frame, \*Object\) .*grumpy\.Repr at 0x[a-f0-9]+>`},
   120  	}
   121  	for _, cas := range cases {
   122  		re := regexp.MustCompile(cas.wantPattern)
   123  		fun := wrapFuncForTest(func(f *Frame, o *Object) *BaseException {
   124  			s, raised := ToStr(f, o)
   125  			if raised != nil {
   126  				return raised
   127  			}
   128  			if !re.MatchString(s.Value()) {
   129  				t.Errorf("str(%v) = %v, want %v", o, s, re)
   130  			}
   131  			s, raised = Repr(f, o)
   132  			if raised != nil {
   133  				return raised
   134  			}
   135  			if !re.MatchString(s.Value()) {
   136  				t.Errorf("repr(%v) = %v, want %v", o, s, re)
   137  			}
   138  			return nil
   139  		})
   140  		if err := runInvokeTestCase(fun, &invokeTestCase{args: cas.args, want: None}); err != "" {
   141  			t.Error(err)
   142  		}
   143  	}
   144  }
   145  
   146  func TestNativeNew(t *testing.T) {
   147  	fun := wrapFuncForTest(func(f *Frame, t *Type, args ...*Object) (*Tuple, *BaseException) {
   148  		o, raised := t.Call(f, args, nil)
   149  		if raised != nil {
   150  			return nil, raised
   151  		}
   152  		return newTestTuple(o, o.Type()), nil
   153  	})
   154  	type testBool bool
   155  	testBoolType := getNativeType(reflect.TypeOf(testBool(false)))
   156  	type testFloat float32
   157  	testFloatType := getNativeType(reflect.TypeOf(testFloat(0)))
   158  	type testString string
   159  	testStringType := getNativeType(reflect.TypeOf(testString("")))
   160  	cases := []invokeTestCase{
   161  		{args: wrapArgs(testBoolType), want: newTestTuple(false, testBoolType).ToObject()},
   162  		{args: wrapArgs(testBoolType, ""), want: newTestTuple(false, testBoolType).ToObject()},
   163  		{args: wrapArgs(testBoolType, 123), want: newTestTuple(true, testBoolType).ToObject()},
   164  		{args: wrapArgs(testBoolType, "foo", "bar"), wantExc: mustCreateException(TypeErrorType, "testBool() takes at most 1 argument (2 given)")},
   165  		{args: wrapArgs(testFloatType), want: newTestTuple(0.0, testFloatType).ToObject()},
   166  		{args: wrapArgs(testFloatType, 3.14), want: newTestTuple(3.14, testFloatType).ToObject()},
   167  		{args: wrapArgs(testFloatType, "foo", "bar"), wantExc: mustCreateException(TypeErrorType, "'__new__' of 'float' requires 0 or 1 arguments")},
   168  		{args: wrapArgs(testStringType), want: newTestTuple("", testStringType).ToObject()},
   169  		{args: wrapArgs(testStringType, "foo"), want: newTestTuple("foo", testStringType).ToObject()},
   170  		{args: wrapArgs(testStringType, "foo", "bar"), wantExc: mustCreateException(TypeErrorType, "str() takes at most 1 argument (2 given)")},
   171  	}
   172  	for _, cas := range cases {
   173  		if err := runInvokeTestCase(fun, &cas); err != "" {
   174  			t.Error(err)
   175  		}
   176  	}
   177  }
   178  
   179  func TestNativeSliceIter(t *testing.T) {
   180  	fun := wrapFuncForTest(func(f *Frame, slice interface{}) (*Object, *BaseException) {
   181  		o, raised := WrapNative(f, reflect.ValueOf(slice))
   182  		if raised != nil {
   183  			return nil, raised
   184  		}
   185  		return TupleType.Call(f, []*Object{o}, nil)
   186  	})
   187  	o := newObject(ObjectType)
   188  	cases := []invokeTestCase{
   189  		{args: wrapArgs([]int{}), want: NewTuple().ToObject()},
   190  		{args: wrapArgs([]string{"foo", "bar"}), want: newTestTuple("foo", "bar").ToObject()},
   191  		{args: wrapArgs([]*Object{True.ToObject(), o}), want: newTestTuple(true, o).ToObject()},
   192  	}
   193  	for _, cas := range cases {
   194  		if err := runInvokeTestCase(fun, &cas); err != "" {
   195  			t.Error(err)
   196  		}
   197  	}
   198  }
   199  
   200  func TestSliceIteratorIter(t *testing.T) {
   201  	iter := newSliceIterator(reflect.ValueOf([]*Object{}))
   202  	cas := &invokeTestCase{args: wrapArgs(iter), want: iter}
   203  	if err := runInvokeMethodTestCase(sliceIteratorType, "__iter__", cas); err != "" {
   204  		t.Error(err)
   205  	}
   206  }
   207  
   208  func TestWrapNative(t *testing.T) {
   209  	o := newObject(ObjectType)
   210  	d := NewDict()
   211  	i := 0
   212  	n := &native{Object{typ: nativeType}, reflect.ValueOf(&i)}
   213  	cases := []struct {
   214  		value   interface{}
   215  		want    *Object
   216  		wantExc *BaseException
   217  	}{
   218  		{true, True.ToObject(), nil},
   219  		{True, True.ToObject(), nil},
   220  		{123, NewInt(123).ToObject(), nil},
   221  		{int8(10), NewInt(10).ToObject(), nil},
   222  		{float32(0.5), NewFloat(0.5).ToObject(), nil},
   223  		{NewFloat(3.14), NewFloat(3.14).ToObject(), nil},
   224  		{uint(MaxInt), NewInt(MaxInt).ToObject(), nil},
   225  		{"foobar", NewStr("foobar").ToObject(), nil},
   226  		{NewStr("foo"), NewStr("foo").ToObject(), nil},
   227  		{uint64(MaxInt) + 100, NewLong(new(big.Int).SetUint64(uint64(MaxInt) + 100)).ToObject(), nil},
   228  		{o, o, nil},
   229  		{d, d.ToObject(), nil},
   230  		{(*Object)(nil), None, nil},
   231  		{uintptr(123), NewInt(123).ToObject(), nil},
   232  		{n, n.ToObject(), nil},
   233  		{(chan int)(nil), None, nil},
   234  		{[]rune("hola"), NewUnicode("hola").ToObject(), nil},
   235  		{big.NewInt(12345), NewLong(big.NewInt(12345)).ToObject(), nil},
   236  		{*big.NewInt(12345), NewLong(big.NewInt(12345)).ToObject(), nil},
   237  	}
   238  	for _, cas := range cases {
   239  		fun := wrapFuncForTest(func(f *Frame) (*Object, *BaseException) {
   240  			return WrapNative(f, reflect.ValueOf(cas.value))
   241  		})
   242  		testCase := invokeTestCase{want: cas.want, wantExc: cas.wantExc}
   243  		if err := runInvokeTestCase(fun, &testCase); err != "" {
   244  			t.Error(err)
   245  		}
   246  	}
   247  }
   248  
   249  func TestWrapNativeFunc(t *testing.T) {
   250  	foo := func() int { return 42 }
   251  	wrappedFoo := mustNotRaise(WrapNative(NewRootFrame(), reflect.ValueOf(foo)))
   252  	if err := runInvokeTestCase(wrappedFoo, &invokeTestCase{want: NewInt(42).ToObject()}); err != "" {
   253  		t.Error(err)
   254  	}
   255  }
   256  
   257  func TestWrapNativeInterface(t *testing.T) {
   258  	// This seems to be the simplest way to get a reflect.Value that has
   259  	// Interface kind.
   260  	iVal := reflect.ValueOf(func() error { return errors.New("foo") }).Call(nil)[0]
   261  	if iVal.Kind() != reflect.Interface {
   262  		t.Fatalf("iVal.Kind() = %v, want interface", iVal.Kind())
   263  	}
   264  	o := mustNotRaise(WrapNative(NewRootFrame(), iVal))
   265  	cas := &invokeTestCase{args: wrapArgs(o), want: NewStr("foo").ToObject()}
   266  	if err := runInvokeMethodTestCase(o.typ, "Error", cas); err != "" {
   267  		t.Error(err)
   268  	}
   269  	// Also test the nil interface case.
   270  	nilVal := reflect.ValueOf(func() error { return nil }).Call(nil)[0]
   271  	if nilVal.Kind() != reflect.Interface {
   272  		t.Fatalf("nilVal.Kind() = %v, want interface", nilVal.Kind())
   273  	}
   274  	if o := mustNotRaise(WrapNative(NewRootFrame(), nilVal)); o != None {
   275  		t.Errorf("WrapNative(%v) = %v, want None", nilVal, o)
   276  	}
   277  }
   278  
   279  func TestWrapNativeOpaque(t *testing.T) {
   280  	type fooStruct struct{}
   281  	foo := &fooStruct{}
   282  	fooVal := reflect.ValueOf(foo)
   283  	fun := wrapFuncForTest(func(f *Frame) *BaseException {
   284  		o, raised := WrapNative(f, fooVal)
   285  		if raised != nil {
   286  			return raised
   287  		}
   288  		if !o.isInstance(nativeType) {
   289  			t.Errorf("WrapNative(%v) = %v, want %v", fooVal, o, foo)
   290  		} else if v := toNativeUnsafe(o).value; v.Type() != reflect.TypeOf(foo) {
   291  			t.Errorf("WrapNative(%v) = %v, want %v", fooVal, v, foo)
   292  		} else if got := v.Interface().(*fooStruct); got != foo {
   293  			t.Errorf("WrapNative(%v) = %v, want %v", fooVal, got, foo)
   294  		}
   295  		return nil
   296  	})
   297  	if err := runInvokeTestCase(fun, &invokeTestCase{want: None}); err != "" {
   298  		t.Error(err)
   299  	}
   300  }
   301  
   302  func TestGetNativeTypeCaches(t *testing.T) {
   303  	foo := []struct{}{}
   304  	typ := getNativeType(reflect.TypeOf(foo))
   305  	if got := getNativeType(reflect.TypeOf(foo)); got != typ {
   306  		t.Errorf("getNativeType(foo) = %v, want %v", got, typ)
   307  	}
   308  }
   309  
   310  func TestGetNativeTypeFunc(t *testing.T) {
   311  	if typ := getNativeType(reflect.TypeOf(func() {})); !typ.isSubclass(nativeFuncType) {
   312  		t.Errorf("getNativeType(func() {}) = %v, want a subclass of func", typ)
   313  	} else if name := typ.Name(); name != "func()" {
   314  		t.Errorf(`%v.__name__ == %q, want "func()"`, typ, name)
   315  	}
   316  }
   317  
   318  type testNativeType struct {
   319  	data int64
   320  }
   321  
   322  func (n *testNativeType) Int64() int64 {
   323  	return n.data
   324  }
   325  
   326  func TestGetNativeTypeMethods(t *testing.T) {
   327  	fun := wrapFuncForTest(func(f *Frame, o *Object) (*Object, *BaseException) {
   328  		if raised := Assert(f, GetBool(o.isInstance(nativeType)).ToObject(), nil); raised != nil {
   329  			return nil, raised
   330  		}
   331  		int64Method, raised := GetAttr(f, o.Type().ToObject(), NewStr("Int64"), nil)
   332  		if raised != nil {
   333  			return nil, raised
   334  		}
   335  		return int64Method.Call(f, []*Object{o}, nil)
   336  	})
   337  	cas := invokeTestCase{args: wrapArgs(&testNativeType{12}), want: NewInt(12).ToObject()}
   338  	if err := runInvokeTestCase(fun, &cas); err != "" {
   339  		t.Error(err)
   340  	}
   341  }
   342  
   343  func TestGetNativeTypeSlice(t *testing.T) {
   344  	if typ := getNativeType(reflect.TypeOf([]int{})); !typ.isSubclass(nativeSliceType) {
   345  		t.Errorf("getNativeType([]int) = %v, want a subclass of slice", typ)
   346  	} else if name := typ.Name(); name != "[]int" {
   347  		t.Errorf(`%v.__name__ == %q, want "func()"`, typ, name)
   348  	}
   349  }
   350  
   351  func TestGetNativeTypeTypedefs(t *testing.T) {
   352  	type testBool bool
   353  	type testInt int
   354  	type testFloat float32
   355  	type testString string
   356  	cases := []struct {
   357  		rtype reflect.Type
   358  		super *Type
   359  	}{
   360  		{reflect.TypeOf(testBool(true)), BoolType},
   361  		{reflect.TypeOf(testFloat(3.14)), FloatType},
   362  		{reflect.TypeOf(testInt(42)), IntType},
   363  		{reflect.TypeOf(testString("foo")), StrType},
   364  	}
   365  	for _, cas := range cases {
   366  		if typ := getNativeType(cas.rtype); typ == cas.super || !typ.isSubclass(cas.super) {
   367  			t.Errorf("getNativeType(%v) = %v, want a subclass of %v", cas.rtype, typ, cas.super)
   368  		}
   369  	}
   370  }
   371  
   372  func TestGetNativeTypeBigInts(t *testing.T) {
   373  	cases := []struct {
   374  		rtype reflect.Type
   375  		typ   *Type
   376  	}{
   377  		{reflect.TypeOf(big.Int{}), LongType},
   378  		{reflect.TypeOf((*big.Int)(nil)), LongType},
   379  	}
   380  	for _, cas := range cases {
   381  		if typ := getNativeType(cas.rtype); typ != cas.typ {
   382  			t.Errorf("getNativeType(%v) = %v, want %v", cas.rtype, typ, cas.typ)
   383  		}
   384  	}
   385  }
   386  
   387  func TestMaybeConvertValue(t *testing.T) {
   388  	type fooStruct struct{}
   389  	foo := &fooStruct{}
   390  	fooNative := &native{Object{typ: nativeType}, reflect.ValueOf(&foo)}
   391  	cases := []struct {
   392  		o             *Object
   393  		expectedRType reflect.Type
   394  		want          interface{}
   395  		wantExc       *BaseException
   396  	}{
   397  		{NewInt(42).ToObject(), reflect.TypeOf(int(0)), 42, nil},
   398  		{NewFloat(0.5).ToObject(), reflect.TypeOf(float32(0)), float32(0.5), nil},
   399  		{fooNative.ToObject(), reflect.TypeOf(&fooStruct{}), foo, nil},
   400  		{None, reflect.TypeOf((*int)(nil)), (*int)(nil), nil},
   401  		{None, reflect.TypeOf(""), nil, mustCreateException(TypeErrorType, "an string is required")},
   402  	}
   403  	for _, cas := range cases {
   404  		fun := wrapFuncForTest(func(f *Frame) *BaseException {
   405  			got, raised := maybeConvertValue(f, cas.o, cas.expectedRType)
   406  			if raised != nil {
   407  				return raised
   408  			}
   409  			if !got.IsValid() || !reflect.DeepEqual(got.Interface(), cas.want) {
   410  				t.Errorf("maybeConvertValue(%v, %v) = %v, want %v", cas.o, nativeTypeName(cas.expectedRType), got, cas.want)
   411  			}
   412  			return nil
   413  		})
   414  		testCase := invokeTestCase{}
   415  		if cas.wantExc != nil {
   416  			testCase.wantExc = cas.wantExc
   417  		} else {
   418  			testCase.want = None
   419  		}
   420  		if err := runInvokeTestCase(fun, &testCase); err != "" {
   421  			t.Error(err)
   422  		}
   423  	}
   424  }
   425  
   426  func TestNativeTypedefNative(t *testing.T) {
   427  	fun := wrapFuncForTest(func(f *Frame, o *Object, wantType reflect.Type) (bool, *BaseException) {
   428  		val, raised := ToNative(f, o)
   429  		if raised != nil {
   430  			return false, raised
   431  		}
   432  		return val.Type() == wantType, nil
   433  	})
   434  	type testBool bool
   435  	testBoolRType := reflect.TypeOf(testBool(false))
   436  	type testInt int
   437  	testIntRType := reflect.TypeOf(testInt(0))
   438  	cases := []invokeTestCase{
   439  		{args: wrapArgs(mustNotRaise(getNativeType(testBoolRType).Call(NewRootFrame(), wrapArgs(true), nil)), testBoolRType), want: True.ToObject()},
   440  		{args: wrapArgs(mustNotRaise(getNativeType(testIntRType).Call(NewRootFrame(), wrapArgs(123), nil)), testIntRType), want: True.ToObject()},
   441  	}
   442  	for _, cas := range cases {
   443  		if err := runInvokeTestCase(fun, &cas); err != "" {
   444  			t.Error(err)
   445  		}
   446  	}
   447  }
   448  
   449  func TestNativeTypeName(t *testing.T) {
   450  	type fooStruct struct{}
   451  	cases := []struct {
   452  		rtype reflect.Type
   453  		want  string
   454  	}{
   455  		{reflect.TypeOf([4]int{}), "[4]int"},
   456  		{reflect.TypeOf(make(chan *string)), "chan *string"},
   457  		{reflect.TypeOf(func() {}), "func()"},
   458  		{reflect.TypeOf(func(int, string) {}), "func(int, string)"},
   459  		{reflect.TypeOf(func() int { return 0 }), "func() int"},
   460  		{reflect.TypeOf(func() (int, float32) { return 0, 0.0 }), "func() (int, float32)"},
   461  		{reflect.TypeOf(map[int]fooStruct{}), "map[int]fooStruct"},
   462  		{reflect.TypeOf(&fooStruct{}), "*fooStruct"},
   463  		{reflect.TypeOf([]byte{}), "[]uint8"},
   464  		{reflect.TypeOf(struct{}{}), "anonymous struct"},
   465  	}
   466  	for _, cas := range cases {
   467  		if got := nativeTypeName(cas.rtype); got != cas.want {
   468  			t.Errorf("nativeTypeName(%v) = %q, want %q", cas.rtype, got, cas.want)
   469  		}
   470  	}
   471  }
   472  
   473  func TestNewNativeFieldChecksInstanceType(t *testing.T) {
   474  	f := NewRootFrame()
   475  
   476  	// Given a native object
   477  	native, raised := WrapNative(f, reflect.ValueOf(struct{ foo string }{}))
   478  	if raised != nil {
   479  		t.Fatal("Unexpected exception:", raised)
   480  	}
   481  
   482  	// When its field property is assigned to a different type
   483  	property, raised := native.typ.Dict().GetItemString(f, "foo")
   484  	if raised != nil {
   485  		t.Fatal("Unexpected exception:", raised)
   486  	}
   487  	if raised := IntType.Dict().SetItemString(f, "foo", property); raised != nil {
   488  		t.Fatal("Unexpected exception:", raised)
   489  	}
   490  
   491  	// And we try to access that property on an object of the new type
   492  	_, raised = GetAttr(f, NewInt(1).ToObject(), NewStr("foo"), nil)
   493  
   494  	// Then expect a TypeError was raised
   495  	if raised == nil || raised.Type() != TypeErrorType {
   496  		t.Fatal("Wanted TypeError; got:", raised)
   497  	}
   498  }
   499  
   500  func TestNativeSliceGetItem(t *testing.T) {
   501  	testRange := make([]int, 20)
   502  	for i := 0; i < len(testRange); i++ {
   503  		testRange[i] = i
   504  	}
   505  	badIndexType := newTestClass("badIndex", []*Type{ObjectType}, newStringDict(map[string]*Object{
   506  		"__index__": newBuiltinFunction("__index__", func(f *Frame, _ Args, _ KWArgs) (*Object, *BaseException) {
   507  			return nil, f.RaiseType(ValueErrorType, "wut")
   508  		}).ToObject(),
   509  	}))
   510  	cases := []invokeTestCase{
   511  		{args: wrapArgs(testRange, 0), want: NewInt(0).ToObject()},
   512  		{args: wrapArgs(testRange, 19), want: NewInt(19).ToObject()},
   513  		{args: wrapArgs([]struct{}{}, 101), wantExc: mustCreateException(IndexErrorType, "index out of range")},
   514  		{args: wrapArgs([]bool{true}, None), wantExc: mustCreateException(TypeErrorType, "native slice indices must be integers, not NoneType")},
   515  		{args: wrapArgs(testRange, newObject(badIndexType)), wantExc: mustCreateException(ValueErrorType, "wut")},
   516  	}
   517  	for _, cas := range cases {
   518  		if err := runInvokeTestCase(wrapFuncForTest(GetItem), &cas); err != "" {
   519  			t.Error(err)
   520  		}
   521  	}
   522  }
   523  
   524  func TestNativeSliceGetItemSlice(t *testing.T) {
   525  	fun := wrapFuncForTest(func(f *Frame, o *Object, slice *Slice, want interface{}) *BaseException {
   526  		item, raised := GetItem(f, o, slice.ToObject())
   527  		if raised != nil {
   528  			return raised
   529  		}
   530  		val, raised := ToNative(f, item)
   531  		if raised != nil {
   532  			return raised
   533  		}
   534  		v := val.Interface()
   535  		msg := fmt.Sprintf("%v[%v] = %v, want %v", o, slice, v, want)
   536  		return Assert(f, GetBool(reflect.DeepEqual(v, want)).ToObject(), NewStr(msg).ToObject())
   537  	})
   538  	type fooStruct struct {
   539  		Bar int
   540  	}
   541  	cases := []invokeTestCase{
   542  		{args: wrapArgs([]string{}, newTestSlice(50, 100), []string{}), want: None},
   543  		{args: wrapArgs([]int{1, 2, 3, 4, 5}, newTestSlice(1, 3, None), []int{2, 3}), want: None},
   544  		{args: wrapArgs([]fooStruct{fooStruct{1}, fooStruct{10}}, newTestSlice(-1, None, None), []fooStruct{fooStruct{10}}), want: None},
   545  		{args: wrapArgs([]int{1, 2, 3, 4, 5}, newTestSlice(1, None, 2), []int{2, 4}), want: None},
   546  		{args: wrapArgs([]float64{1.0, 2.0, 3.0, 4.0, 5.0}, newTestSlice(big.NewInt(1), None, 2), []float64{2.0, 4.0}), want: None},
   547  		{args: wrapArgs([]string{"1", "2", "3", "4", "5"}, newTestSlice(1, big.NewInt(5), 2), []string{"2", "4"}), want: None},
   548  		{args: wrapArgs([]int{1, 2, 3, 4, 5}, newTestSlice(1, None, big.NewInt(2)), []int{2, 4}), want: None},
   549  		{args: wrapArgs([]int16{1, 2, 3, 4, 5}, newTestSlice(1.0, 3, None), None), wantExc: mustCreateException(TypeErrorType, errBadSliceIndex)},
   550  		{args: wrapArgs([]byte{1, 2, 3}, newTestSlice(1, None, 0), None), wantExc: mustCreateException(ValueErrorType, "slice step cannot be zero")},
   551  	}
   552  	for _, cas := range cases {
   553  		if err := runInvokeTestCase(fun, &cas); err != "" {
   554  			t.Error(err)
   555  		}
   556  	}
   557  }
   558  
   559  func TestNativeSliceLen(t *testing.T) {
   560  	cases := []invokeTestCase{
   561  		{args: wrapArgs([]string{"foo", "bar"}), want: NewInt(2).ToObject()},
   562  		{args: wrapArgs(make([]int, 100)), want: NewInt(100).ToObject()},
   563  	}
   564  	for _, cas := range cases {
   565  		if err := runInvokeTestCase(wrapFuncForTest(Len), &cas); err != "" {
   566  			t.Error(err)
   567  		}
   568  	}
   569  }
   570  
   571  func TestNativeSliceStrRepr(t *testing.T) {
   572  	slice := make([]*Object, 2)
   573  	o := mustNotRaise(WrapNative(NewRootFrame(), reflect.ValueOf(slice)))
   574  	slice[0] = o
   575  	slice[1] = NewStr("foo").ToObject()
   576  	cases := []invokeTestCase{
   577  		{args: wrapArgs([]string{"foo", "bar"}), want: NewStr("[]string{'foo', 'bar'}").ToObject()},
   578  		{args: wrapArgs([]uint16{123}), want: NewStr("[]uint16{123}").ToObject()},
   579  		{args: wrapArgs(o), want: NewStr("[]*Object{[]*Object{...}, 'foo'}").ToObject()},
   580  	}
   581  	for _, cas := range cases {
   582  		if err := runInvokeTestCase(wrapFuncForTest(ToStr), &cas); err != "" {
   583  			t.Error(err)
   584  		}
   585  		if err := runInvokeTestCase(wrapFuncForTest(Repr), &cas); err != "" {
   586  			t.Error(err)
   587  		}
   588  	}
   589  }
   590  
   591  func TestNativeSliceSetItemSlice(t *testing.T) {
   592  	fun := wrapFuncForTest(func(f *Frame, o, index, value *Object, want interface{}) *BaseException {
   593  		originalStr := o.String()
   594  		if raised := SetItem(f, o, index, value); raised != nil {
   595  			return raised
   596  		}
   597  		val, raised := ToNative(f, o)
   598  		if raised != nil {
   599  			return raised
   600  		}
   601  		v := val.Interface()
   602  		msg := fmt.Sprintf("%v[%v] = %v -> %v, want %v", originalStr, index, value, o, want)
   603  		return Assert(f, GetBool(reflect.DeepEqual(v, want)).ToObject(), NewStr(msg).ToObject())
   604  	})
   605  	type fooStruct struct {
   606  		bar []int
   607  	}
   608  	foo := fooStruct{[]int{1, 2, 3}}
   609  	bar := mustNotRaise(WrapNative(NewRootFrame(), reflect.ValueOf(foo).Field(0)))
   610  	cases := []invokeTestCase{
   611  		{args: wrapArgs([]string{"foo", "bar"}, 1, "baz", []string{"foo", "baz"}), want: None},
   612  		{args: wrapArgs([]uint16{1, 2, 3}, newTestSlice(1), newTestList(4), []uint16{4, 2, 3}), want: None},
   613  		{args: wrapArgs([]int{1, 2, 4, 5}, newTestSlice(1, None, 2), newTestTuple(10, 20), []int{1, 10, 4, 20}), want: None},
   614  		{args: wrapArgs([]float64{}, newTestSlice(4, 8, 0), NewList(), None), wantExc: mustCreateException(ValueErrorType, "slice step cannot be zero")},
   615  		{args: wrapArgs([]string{"foo", "bar"}, -100, None, None), wantExc: mustCreateException(IndexErrorType, "index out of range")},
   616  		{args: wrapArgs([]int{}, 101, None, None), wantExc: mustCreateException(IndexErrorType, "index out of range")},
   617  		{args: wrapArgs([]bool{true}, None, false, None), wantExc: mustCreateException(TypeErrorType, "native slice indices must be integers, not NoneType")},
   618  		{args: wrapArgs([]int8{1, 2, 3}, newTestSlice(0), []int8{0}, []int8{0, 1, 2, 3}), wantExc: mustCreateException(ValueErrorType, "attempt to assign sequence of size 1 to slice of size 0")},
   619  		{args: wrapArgs([]int{1, 2, 3}, newTestSlice(2, None), newTestList("foo"), None), wantExc: mustCreateException(TypeErrorType, "an int is required")},
   620  		{args: wrapArgs(bar, 1, 42, None), wantExc: mustCreateException(TypeErrorType, "cannot set slice element")},
   621  		{args: wrapArgs(bar, newTestSlice(1), newTestList(42), None), wantExc: mustCreateException(TypeErrorType, "cannot set slice element")},
   622  		{args: wrapArgs([]string{"foo", "bar"}, 1, 123.0, None), wantExc: mustCreateException(TypeErrorType, "an string is required")},
   623  		{args: wrapArgs([]string{"foo", "bar"}, 1, 123.0, None), wantExc: mustCreateException(TypeErrorType, "an string is required")},
   624  	}
   625  	for _, cas := range cases {
   626  		if err := runInvokeTestCase(fun, &cas); err != "" {
   627  			t.Error(err)
   628  		}
   629  	}
   630  }
   631  
   632  func TestNativeStructFieldGet(t *testing.T) {
   633  	fun := wrapFuncForTest(func(f *Frame, o *Object, attr *Str) (*Object, *BaseException) {
   634  		return GetAttr(f, o, attr, nil)
   635  	})
   636  	type fooStruct struct {
   637  		bar int
   638  		Baz float64
   639  	}
   640  	cases := []invokeTestCase{
   641  		{args: wrapArgs(fooStruct{bar: 1}, "bar"), want: NewInt(1).ToObject()},
   642  		{args: wrapArgs(&fooStruct{Baz: 3.14}, "Baz"), want: NewFloat(3.14).ToObject()},
   643  		{args: wrapArgs(fooStruct{}, "qux"), wantExc: mustCreateException(AttributeErrorType, `'fooStruct' object has no attribute 'qux'`)},
   644  	}
   645  	for _, cas := range cases {
   646  		if err := runInvokeTestCase(fun, &cas); err != "" {
   647  			t.Error(err)
   648  		}
   649  	}
   650  }
   651  
   652  func TestNativeStructFieldSet(t *testing.T) {
   653  	fun := wrapFuncForTest(func(f *Frame, o *Object, attr *Str, value *Object) (*Object, *BaseException) {
   654  		if raised := SetAttr(f, o, attr, value); raised != nil {
   655  			return nil, raised
   656  		}
   657  		return GetAttr(f, o, attr, nil)
   658  	})
   659  	type fooStruct struct {
   660  		bar int
   661  		Baz float64
   662  	}
   663  	cases := []invokeTestCase{
   664  		{args: wrapArgs(&fooStruct{}, "Baz", 1.5), want: NewFloat(1.5).ToObject()},
   665  		{args: wrapArgs(fooStruct{}, "bar", 123), wantExc: mustCreateException(TypeErrorType, `cannot set field 'bar' of type 'fooStruct'`)},
   666  		{args: wrapArgs(fooStruct{}, "qux", "abc"), wantExc: mustCreateException(AttributeErrorType, `'fooStruct' has no attribute 'qux'`)},
   667  		{args: wrapArgs(&fooStruct{}, "Baz", "abc"), wantExc: mustCreateException(TypeErrorType, "an float64 is required")},
   668  	}
   669  	for _, cas := range cases {
   670  		if err := runInvokeTestCase(fun, &cas); err != "" {
   671  			t.Error(err)
   672  		}
   673  	}
   674  }
   675  
   676  func wrapArgs(elems ...interface{}) Args {
   677  	f := NewRootFrame()
   678  	argc := len(elems)
   679  	result := make(Args, argc, argc)
   680  	var raised *BaseException
   681  	for i, elem := range elems {
   682  		if result[i], raised = WrapNative(f, reflect.ValueOf(elem)); raised != nil {
   683  			panic(raised)
   684  		}
   685  	}
   686  	return result
   687  }
   688  
   689  func wrapKWArgs(elems ...interface{}) KWArgs {
   690  	if len(elems)%2 != 0 {
   691  		panic("invalid kwargs")
   692  	}
   693  	numItems := len(elems) / 2
   694  	kwargs := make(KWArgs, numItems, numItems)
   695  	f := NewRootFrame()
   696  	for i := 0; i < numItems; i++ {
   697  		kwargs[i].Name = elems[i*2].(string)
   698  		kwargs[i].Value = mustNotRaise(WrapNative(f, reflect.ValueOf(elems[i*2+1])))
   699  	}
   700  	return kwargs
   701  }