github.com/google/grumpy@v0.0.0-20171122020858-3ec87959189c/runtime/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 grumpy 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 }