test for panic and recover

R=r, adg
CC=golang-dev
https://golang.org/cl/869041
diff --git a/test/recover.go b/test/recover.go
new file mode 100644
index 0000000..8b47b82
--- /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()
+	}
+}
diff --git a/test/recover1.go b/test/recover1.go
new file mode 100644
index 0000000..db58473
--- /dev/null
+++ b/test/recover1.go
@@ -0,0 +1,141 @@
+// $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 recover during recursive panics.
+// Here be dragons.
+
+package main
+
+import "runtime"
+
+func main() {
+	test1()
+	test2()
+	test3()
+	test4()
+	test5()
+	test6()
+	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() {
+	// Easy nested recursive panic.
+	defer mustRecover(1)
+	defer func() {
+		defer mustRecover(2)
+		panic(2)
+	}()
+	panic(1)
+}
+
+func test2() {
+	// Sequential panic.
+	defer mustNotRecover()
+	defer func() {
+		v := recover()
+		if v == nil || v.(int) != 2 {
+			println("wrong value", v, 2)
+			die()
+		}
+		defer mustRecover(3)
+		panic(3)
+	}()
+	panic(2)
+}
+
+func test3() {
+	// Sequential panic - like test2 but less picky.
+	defer mustNotRecover()
+	defer func() {
+		recover()
+		defer mustRecover(3)
+		panic(3)
+	}()
+	panic(2)
+}
+
+func test4() {
+	// Single panic.
+	defer mustNotRecover()
+	defer func() {
+		recover()
+	}()
+	panic(4)
+}
+
+func test5() {
+	// Single panic but recover called via defer
+	defer mustNotRecover()
+	defer func() {
+		defer recover()
+	}()
+	panic(5)
+}
+
+func test6() {
+	// Sequential panic.
+	// Like test3, but changed recover to defer (same change as test4 → test5).
+	defer mustNotRecover()
+	defer func() {
+		defer recover()	// like a normal call from this func; runs because mustRecover stops the panic
+		defer mustRecover(3)
+		panic(3)
+	}()
+	panic(2)
+}
+
+func test7() {
+	// Like test6, but swapped defer order.
+	// The recover in "defer recover()" is now a no-op,
+	// because it runs called from panic, not from the func,
+	// and therefore cannot see the panic of 2.
+	// (Alternately, it cannot see the panic of 2 because
+	// there is an active panic of 3.  And it cannot see the
+	// panic of 3 because it is at the wrong level (too high on the stack).)
+	defer mustRecover(2)
+	defer func() {
+		defer mustRecover(3)
+		defer recover()	// now a no-op, unlike in test6.
+		panic(3)
+	}()
+	panic(2)
+}