github.com/pygolin/runtime@v0.0.0-20201208210830-a62e3cd39798/frame_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 runtime
    16  
    17  import (
    18  	"fmt"
    19  	"reflect"
    20  	"testing"
    21  )
    22  
    23  const (
    24  	testRunStateInvalid RunState = -1
    25  	testRunStateStart            = 0
    26  	testRunStateDone             = 1
    27  )
    28  
    29  func TestFrameArgsCache(t *testing.T) {
    30  	f := NewRootFrame()
    31  	args1 := f.MakeArgs(0)
    32  	if args1 != nil {
    33  		t.Errorf("f.MakeArgs(0) = %v, want nil", args1)
    34  	}
    35  	args2 := f.MakeArgs(1)
    36  	if argc := len(args2); argc != 1 {
    37  		t.Errorf("f.MakeArgs(1) had len %d, want len 1", argc)
    38  	}
    39  	if arg0 := args2[0]; arg0 != nil {
    40  		t.Errorf("f.MakeArgs(1)[0] = %v, want nil", arg0)
    41  	}
    42  	args2[0] = None // Make sure this is cleared in MakeArgs result below.
    43  	f.FreeArgs(args2)
    44  	args3 := f.MakeArgs(1)
    45  	if &args2[0] != &args3[0] {
    46  		t.Error("freed arg slice not returned from cache")
    47  	}
    48  	if arg0 := args3[0]; arg0 != nil {
    49  		t.Errorf("f.MakeArgs(1)[0] = %v, want nil", arg0)
    50  	}
    51  	args4 := f.MakeArgs(1000)
    52  	if argc := len(args4); argc != 1000 {
    53  		t.Errorf("f.MakeArgs(1000) had len %d, want len 1", argc)
    54  	}
    55  	// Make sure the cache doesn't overflow when overfed.
    56  	for i := 0; i < 100; i++ {
    57  		f.FreeArgs(make(Args, argsCacheArgc))
    58  	}
    59  	args5 := f.MakeArgs(2)
    60  	if argc := len(args5); argc != 2 {
    61  		t.Errorf("f.MakeArgs(2) had len %d, want len 2", argc)
    62  	}
    63  }
    64  
    65  func TestFramePopCheckpoint(t *testing.T) {
    66  	cases := []struct {
    67  		states  []RunState
    68  		want    RunState
    69  		wantTop RunState
    70  	}{
    71  		{nil, testRunStateInvalid, testRunStateInvalid},
    72  		{[]RunState{testRunStateDone}, testRunStateDone, testRunStateInvalid},
    73  		{[]RunState{testRunStateDone, testRunStateStart}, testRunStateStart, testRunStateDone},
    74  	}
    75  	for _, cas := range cases {
    76  		f := NewRootFrame()
    77  		for _, state := range cas.states {
    78  			f.PushCheckpoint(state)
    79  		}
    80  		f.PopCheckpoint()
    81  		if got := f.State(); got != cas.want {
    82  			t.Errorf("%#v.Pop() = %v, want %v", f, got, cas.want)
    83  		} else if numCheckpoints := len(f.checkpoints); numCheckpoints == 0 && cas.wantTop != testRunStateInvalid {
    84  			t.Errorf("%#v.Pop() left checkpoint stack empty, wanted top to be %v", f, cas.wantTop)
    85  		} else if numCheckpoints != 0 && f.checkpoints[numCheckpoints-1] != cas.wantTop {
    86  			t.Errorf("%#v.Pop() left checkpoint stack with top %v, want %v", f, f.State(), cas.wantTop)
    87  		}
    88  	}
    89  }
    90  
    91  func TestFramePushCheckpoint(t *testing.T) {
    92  	f := NewRootFrame()
    93  	states := []RunState{testRunStateStart, testRunStateDone}
    94  	for _, state := range states {
    95  		f.PushCheckpoint(state)
    96  		if numCheckpoints := len(f.checkpoints); numCheckpoints == 0 {
    97  			t.Errorf("%#v.Push(%v) left checkpoint stack empty, want non-empty", f, state)
    98  		} else if top := f.checkpoints[numCheckpoints-1]; top != state {
    99  			t.Errorf("%#v.Push(%v) left checkpoint stack top %v, want %v", f, state, top, state)
   100  		}
   101  	}
   102  }
   103  
   104  func TestFrameRaise(t *testing.T) {
   105  	f := NewRootFrame()
   106  	raisedFrame := NewRootFrame()
   107  	raisedFrame.RestoreExc(mustCreateException(ValueErrorType, "foo"), newTraceback(raisedFrame, nil))
   108  	tb := newTraceback(f, nil)
   109  	multiArgExc := toBaseExceptionUnsafe(mustNotRaise(ExceptionType.Call(f, []*Object{None, None}, nil)))
   110  	barType := newTestClass("Bar", []*Type{ExceptionType}, newStringDict(map[string]*Object{
   111  		"__new__": newBuiltinFunction("__new__", func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
   112  			return NewStr("Bar").ToObject(), nil
   113  		}).ToObject(),
   114  	}))
   115  	otherTB := newTraceback(NewRootFrame(), nil)
   116  	cases := []struct {
   117  		f       *Frame
   118  		typ     *Object
   119  		inst    *Object
   120  		tb      *Object
   121  		wantExc *BaseException
   122  		wantTB  *Traceback
   123  	}{
   124  		{f, nil, nil, nil, mustCreateException(TypeErrorType, `exceptions must be derived from BaseException, not "NoneType"`), tb},
   125  		{raisedFrame, nil, nil, nil, mustCreateException(ValueErrorType, "foo"), newTraceback(raisedFrame, nil)},
   126  		{f, ExceptionType.ToObject(), nil, nil, mustCreateException(ExceptionType, ""), tb},
   127  		{f, newObject(ExceptionType), nil, nil, toBaseExceptionUnsafe(newObject(ExceptionType)), tb},
   128  		{f, NewInt(42).ToObject(), nil, nil, mustCreateException(TypeErrorType, `exceptions must be derived from BaseException, not "int"`), tb},
   129  		{f, ObjectType.ToObject(), nil, nil, mustCreateException(TypeErrorType, `exceptions must be derived from BaseException, not "object"`), tb},
   130  		{f, AssertionErrorType.ToObject(), NewStr("foo").ToObject(), nil, mustCreateException(AssertionErrorType, "foo"), tb},
   131  		{f, ExceptionType.ToObject(), NewTuple(None, None).ToObject(), nil, multiArgExc, tb},
   132  		{f, ExceptionType.ToObject(), mustCreateException(KeyErrorType, "foo").ToObject(), nil, mustCreateException(KeyErrorType, "foo"), tb},
   133  		{f, barType.ToObject(), nil, nil, mustCreateException(TypeErrorType, `exceptions must be derived from BaseException, not "str"`), tb},
   134  		{f, newObject(StopIterationType), NewInt(123).ToObject(), nil, mustCreateException(TypeErrorType, "instance exception may not have a separate value"), tb},
   135  		{f, RuntimeErrorType.ToObject(), nil, otherTB.ToObject(), mustCreateException(RuntimeErrorType, ""), otherTB},
   136  		{f, RuntimeErrorType.ToObject(), nil, newObject(ObjectType), mustCreateException(TypeErrorType, "raise: arg 3 must be a traceback or None"), tb},
   137  	}
   138  	for _, cas := range cases {
   139  		call := fmt.Sprintf("frame.Raise(%v, %v, %v)", cas.typ, cas.inst, cas.tb)
   140  		// Not using cas.f here because the test may require
   141  		// cas.f is uncleared. If a fresh frame is desired for
   142  		// a particular test, use f.
   143  		f.RestoreExc(nil, nil)
   144  		cas.f.Raise(cas.typ, cas.inst, cas.tb)
   145  		if got := cas.f.Raise(cas.typ, cas.inst, cas.tb); !exceptionsAreEquivalent(got, cas.wantExc) {
   146  			t.Errorf("%s raised %v, want %v", call, got, cas.wantExc)
   147  		} else if e, gotTB := cas.f.ExcInfo(); got != e {
   148  			t.Errorf("%s raised %v but ExcInfo returned %v", call, got, e)
   149  		} else if !reflect.DeepEqual(gotTB, cas.wantTB) {
   150  			t.Errorf("%s produced traceback %v, want %v", call, gotTB, cas.wantTB)
   151  		}
   152  	}
   153  }
   154  
   155  func TestFrameRaiseType(t *testing.T) {
   156  	fun := newBuiltinFunction("TestFrameRaiseType", func(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   157  		if raised := checkFunctionArgs(f, "TestFrameRaiseType", args, TypeType, StrType); raised != nil {
   158  			return nil, raised
   159  		}
   160  		return nil, f.RaiseType(toTypeUnsafe(args[0]), toStrUnsafe(args[1]).Value())
   161  	}).ToObject()
   162  	cases := []invokeTestCase{
   163  		{args: wrapArgs(TypeErrorType, "bar"), wantExc: mustCreateException(TypeErrorType, "bar")},
   164  		{args: wrapArgs(ExceptionType, ""), wantExc: toBaseExceptionUnsafe(mustNotRaise(ExceptionType.Call(NewRootFrame(), wrapArgs(""), nil)))},
   165  		{args: wrapArgs(TupleType, "foo"), wantExc: mustCreateException(TypeErrorType, `exceptions must be derived from BaseException, not "tuple"`)},
   166  	}
   167  	for _, cas := range cases {
   168  		if err := runInvokeTestCase(fun, &cas); err != "" {
   169  			t.Error(err)
   170  		}
   171  	}
   172  }
   173  
   174  func TestReprEnterLeave(t *testing.T) {
   175  	o := newObject(ObjectType)
   176  	parent := NewRootFrame()
   177  	child := newChildFrame(parent)
   178  	wantParent := NewRootFrame()
   179  	wantParent.reprState = map[*Object]bool{o: true}
   180  	child.reprEnter(o)
   181  	// After child.reprEnter(), expect the parent's reprState to contain o.
   182  	if wantChild := newChildFrame(parent); !reflect.DeepEqual(child, wantChild) {
   183  		t.Errorf("reprEnter: child frame was %#v, want %#v", child, wantChild)
   184  	} else if !reflect.DeepEqual(parent, wantParent) {
   185  		t.Errorf("reprEnter: parent frame was %#v, want %#v", parent, wantParent)
   186  	} else {
   187  		wantParent.reprState = map[*Object]bool{}
   188  		child.reprLeave(o)
   189  		// Expect the parent's reprState to be empty after reprLeave().
   190  		if wantChild := newChildFrame(parent); !reflect.DeepEqual(child, wantChild) {
   191  			t.Errorf("reprLeave: child frame was %#v, want %#v", child, wantChild)
   192  		} else if !reflect.DeepEqual(parent, wantParent) {
   193  			t.Errorf("reprLeave: parent frame was %#v, want %#v", parent, wantParent)
   194  		}
   195  	}
   196  }
   197  
   198  func TestFrameRoot(t *testing.T) {
   199  	f1 := NewRootFrame()
   200  	f2 := newChildFrame(f1)
   201  	frames := []*Frame{f1, f2, newChildFrame(f2)}
   202  	for _, f := range frames {
   203  		if f.threadState != f1.threadState {
   204  			t.Errorf("frame threadState was %v, want %v", f.threadState, f1.threadState)
   205  		}
   206  	}
   207  }
   208  
   209  func TestFrameExcInfo(t *testing.T) {
   210  	raisedFrame := NewRootFrame()
   211  	raisedExc := mustCreateException(ValueErrorType, "foo")
   212  	raisedTB := newTraceback(raisedFrame, nil)
   213  	raisedFrame.RestoreExc(raisedExc, raisedTB)
   214  	cases := []invokeTestCase{
   215  		{args: wrapArgs(NewRootFrame()), want: NewTuple(None, None).ToObject()},
   216  		{args: wrapArgs(raisedFrame), want: newTestTuple(raisedExc, raisedTB).ToObject()},
   217  	}
   218  	for _, cas := range cases {
   219  		if err := runInvokeMethodTestCase(FrameType, "__exc_info__", &cas); err != "" {
   220  			t.Error(err)
   221  		}
   222  	}
   223  }
   224  
   225  type checkInvokeResultType int
   226  
   227  const (
   228  	checkInvokeResultOk                  checkInvokeResultType = iota
   229  	checkInvokeResultExceptionMismatch                         = iota
   230  	checkInvokeResultReturnValueMismatch                       = iota
   231  )
   232  
   233  func checkResult(got, want *Object, gotExc, wantExc *BaseException) checkInvokeResultType {
   234  	if !exceptionsAreEquivalent(gotExc, wantExc) {
   235  		return checkInvokeResultExceptionMismatch
   236  	}
   237  	if got == nil && want == nil {
   238  		return checkInvokeResultOk
   239  	}
   240  	if got != nil && want != nil {
   241  		// Compare exceptions for equivalence but other objects using
   242  		// __eq__.
   243  		if got.isInstance(BaseExceptionType) && want.isInstance(BaseExceptionType) &&
   244  			exceptionsAreEquivalent(toBaseExceptionUnsafe(got), toBaseExceptionUnsafe(want)) {
   245  			return checkInvokeResultOk
   246  		}
   247  		f := NewRootFrame()
   248  		eq, raised := Eq(f, got, want)
   249  		if raised != nil {
   250  			panic(raised)
   251  		}
   252  		b, raised := IsTrue(f, eq)
   253  		if raised != nil {
   254  			panic(raised)
   255  		}
   256  		if b {
   257  			return checkInvokeResultOk
   258  		}
   259  	}
   260  	return checkInvokeResultReturnValueMismatch
   261  }
   262  
   263  func checkInvokeResult(callable *Object, args Args, wantRet *Object, wantExc *BaseException) (*Object, checkInvokeResultType) {
   264  	return checkInvokeResultKwargs(callable, args, nil, wantRet, wantExc)
   265  }
   266  
   267  func checkInvokeResultKwargs(callable *Object, args Args, kwargs KWArgs, wantRet *Object, wantExc *BaseException) (*Object, checkInvokeResultType) {
   268  	ret, raised := callable.Call(NewRootFrame(), args, kwargs)
   269  	switch checkResult(ret, wantRet, raised, wantExc) {
   270  	case checkInvokeResultExceptionMismatch:
   271  		if raised == nil {
   272  			return nil, checkInvokeResultExceptionMismatch
   273  		}
   274  		return raised.ToObject(), checkInvokeResultExceptionMismatch
   275  	case checkInvokeResultReturnValueMismatch:
   276  		return ret, checkInvokeResultReturnValueMismatch
   277  	default:
   278  		return nil, checkInvokeResultOk
   279  	}
   280  }
   281  
   282  type invokeTestCase struct {
   283  	args    Args
   284  	kwargs  KWArgs
   285  	want    *Object
   286  	wantExc *BaseException
   287  }
   288  
   289  func runInvokeTestCase(callable *Object, cas *invokeTestCase) string {
   290  	f := NewRootFrame()
   291  	name := mustNotRaise(GetAttr(f, callable, internedName, NewStr("<unknown>").ToObject()))
   292  	if !name.isInstance(StrType) {
   293  		return fmt.Sprintf("%v.__name__ is not a string", callable)
   294  	}
   295  	// Get repr of args before the call in case any of the args are mutated.
   296  	argsRepr, raised := Repr(f, NewTuple(cas.args...).ToObject())
   297  	if raised != nil {
   298  		panic(raised)
   299  	}
   300  	nameStr := toStrUnsafe(name).Value()
   301  	switch got, match := checkInvokeResultKwargs(callable, cas.args, cas.kwargs, cas.want, cas.wantExc); match {
   302  	case checkInvokeResultExceptionMismatch:
   303  		return fmt.Sprintf("%s%s raised %v, want %v", nameStr, argsRepr.Value(), got, cas.wantExc)
   304  	case checkInvokeResultReturnValueMismatch:
   305  		return fmt.Sprintf("%s%s = %v, want %v", nameStr, argsRepr.Value(), got, cas.want)
   306  	default:
   307  		return ""
   308  	}
   309  }
   310  
   311  func runInvokeMethodTestCase(t *Type, methodName string, cas *invokeTestCase) string {
   312  	method := mustNotRaise(GetAttr(NewRootFrame(), t.ToObject(), NewStr(methodName), nil))
   313  	return runInvokeTestCase(method, cas)
   314  }