From 016c23f4d61b6ea21ee7bc79538636c65e434636 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 31 Mar 2010 11:47:09 -0700 Subject: test for panic and recover R=r, adg CC=golang-dev http://codereview.appspot.com/869041 --- test/recover.go | 246 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 test/recover.go (limited to 'test/recover.go') diff --git a/test/recover.go b/test/recover.go new file mode 100644 index 000000000..8b47b8247 --- /dev/null +++ b/test/recover.go @@ -0,0 +1,246 @@ +// $G $D/$F.go && $L $F.$A && ./$A.out + +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test of basic recover functionality. + +package main + +import "runtime" + +func main() { + test1() + test1WithClosures() + test2() + test3() + test4() + test5() + test6() + test6WithClosures() + test7() +} + +func die() { + runtime.Breakpoint() // can't depend on panic +} + +func mustRecover(x interface{}) { + mustNotRecover() // because it's not a defer call + v := recover() + if v == nil { + println("missing recover") + die() // panic is useless here + } + if v != x { + println("wrong value", v, x) + die() + } + + // the value should be gone now regardless + v = recover() + if v != nil { + println("recover didn't recover") + die() + } +} + +func mustNotRecover() { + v := recover() + if v != nil { + println("spurious recover") + die() + } +} + +func withoutRecover() { + mustNotRecover() // because it's a sub-call +} + +func test1() { + defer mustNotRecover() // because mustRecover will squelch it + defer mustRecover(1) // because of panic below + defer withoutRecover() // should be no-op, leaving for mustRecover to find + panic(1) +} + +// Repeat test1 with closures instead of standard function. +// Interesting because recover bases its decision +// on the frame pointer of its caller, and a closure's +// frame pointer is in the middle of its actual arguments +// (after the hidden ones for the closed-over variables). +func test1WithClosures() { + defer func() { + v := recover() + if v != nil { + println("spurious recover in closure") + die() + } + }() + defer func(x interface{}) { + mustNotRecover() + v := recover() + if v == nil { + println("missing recover") + die() + } + if v != x { + println("wrong value", v, x) + die() + } + }(1) + defer func() { + mustNotRecover() + }() + panic(1) +} + +func test2() { + // Recover only sees the panic argument + // if it is called from a deferred call. + // It does not see the panic when called from a call within a deferred call (too late) + // nor does it see the panic when it *is* the deferred call (too early). + defer mustRecover(2) + defer recover() // should be no-op + panic(2) +} + +func test3() { + defer mustNotRecover() + defer func() { + recover() // should squelch + }() + panic(3) +} + +func test4() { + // Equivalent to test3 but using defer to make the call. + defer mustNotRecover() + defer func() { + defer recover() // should squelch + }() + panic(4) +} + +// Check that closures can set output arguments. +// Run g(). If it panics, return x; else return deflt. +func try(g func(), deflt interface{}) (x interface{}) { + defer func() { + if v := recover(); v != nil { + x = v + } + }() + defer g() + return deflt +} + +// Check that closures can set output arguments. +// Run g(). If it panics, return x; else return deflt. +func try1(g func(), deflt interface{}) (x interface{}) { + defer func() { + if v := recover(); v != nil { + x = v + } + }() + defer g() + x = deflt + return +} + +func test5() { + v := try(func() { panic(5) }, 55).(int) + if v != 5 { + println("wrong value", v, 5) + die() + } + + s := try(func() { }, "hi").(string) + if s != "hi" { + println("wrong value", s, "hi") + die() + } + + v = try1(func() { panic(5) }, 55).(int) + if v != 5 { + println("try1 wrong value", v, 5) + die() + } + + s = try1(func() { }, "hi").(string) + if s != "hi" { + println("try1 wrong value", s, "hi") + die() + } +} + +// When a deferred big call starts, it must first +// create yet another stack segment to hold the +// giant frame for x. Make sure that doesn't +// confuse recover. +func big(mustRecover bool) { + var x [100000]int + x[0] = 1 + x[99999] = 1 + _ = x + + v := recover() + if mustRecover { + if v == nil { + println("missing big recover") + die() + } + } else { + if v != nil { + println("spurious big recover") + die() + } + } +} + +func test6() { + defer big(false) + defer big(true) + panic(6) +} + +func test6WithClosures() { + defer func() { + var x [100000]int + x[0] = 1 + x[99999] = 1 + _ = x + if recover() != nil { + println("spurious big closure recover") + die() + } + }() + defer func() { + var x [100000]int + x[0] = 1 + x[99999] = 1 + _ = x + if recover() == nil { + println("missing big closure recover") + die() + } + }() + panic("6WithClosures") +} + +func test7() { + ok := false + func() { + // should panic, then call mustRecover 7, which stops the panic. + // then should keep processing ordinary defers earlier than that one + // before returning. + // this test checks that the defer func on the next line actually runs. + defer func() { ok = true }() + defer mustRecover(7) + panic(7) + }() + if !ok { + println("did not run ok func") + die() + } +} -- cgit v1.2.1