Source file src/runtime/panic_test.go

     1  // Copyright 2020 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package runtime_test
     6  
     7  import (
     8  	"runtime"
     9  	"slices"
    10  	"strings"
    11  	"testing"
    12  	"time"
    13  )
    14  
    15  // Test that panics print out the underlying value
    16  // when the underlying kind is directly printable.
    17  // Issue: https://golang.org/issues/37531
    18  func TestPanicWithDirectlyPrintableCustomTypes(t *testing.T) {
    19  	tests := []struct {
    20  		name            string
    21  		wantPanicPrefix string
    22  	}{
    23  		{"panicCustomBool", `panic: main.MyBool(true)`},
    24  		{"panicCustomComplex128", `panic: main.MyComplex128(32.1+10i)`},
    25  		{"panicCustomComplex64", `panic: main.MyComplex64(0.11+3i)`},
    26  		{"panicCustomFloat32", `panic: main.MyFloat32(-93.7)`},
    27  		{"panicCustomFloat64", `panic: main.MyFloat64(-93.7)`},
    28  		{"panicCustomInt", `panic: main.MyInt(93)`},
    29  		{"panicCustomInt8", `panic: main.MyInt8(93)`},
    30  		{"panicCustomInt16", `panic: main.MyInt16(93)`},
    31  		{"panicCustomInt32", `panic: main.MyInt32(93)`},
    32  		{"panicCustomInt64", `panic: main.MyInt64(93)`},
    33  		{"panicCustomString", `panic: main.MyString("Panic` + "\n\t" + `line two")`},
    34  		{"panicCustomUint", `panic: main.MyUint(93)`},
    35  		{"panicCustomUint8", `panic: main.MyUint8(93)`},
    36  		{"panicCustomUint16", `panic: main.MyUint16(93)`},
    37  		{"panicCustomUint32", `panic: main.MyUint32(93)`},
    38  		{"panicCustomUint64", `panic: main.MyUint64(93)`},
    39  		{"panicCustomUintptr", `panic: main.MyUintptr(93)`},
    40  		{"panicDeferFatal", "panic: runtime.errorString(\"invalid memory address or nil pointer dereference\")\n\tfatal error: sync: unlock of unlocked mutex"},
    41  		{"panicDoublieDeferFatal", "panic: runtime.errorString(\"invalid memory address or nil pointer dereference\") [recovered, repanicked]\n\tfatal error: sync: unlock of unlocked mutex"},
    42  	}
    43  
    44  	for _, tt := range tests {
    45  		t := t
    46  		t.Run(tt.name, func(t *testing.T) {
    47  			output := runTestProg(t, "testprog", tt.name)
    48  			if !strings.HasPrefix(output, tt.wantPanicPrefix) {
    49  				t.Fatalf("%q\nis not present in\n%s", tt.wantPanicPrefix, output)
    50  			}
    51  		})
    52  	}
    53  }
    54  
    55  func TestPanicRecoverSpeed(t *testing.T) {
    56  	// For issue 77062.
    57  	t.Skip("This test is too flaky at the moment. But it does normally pass. Suggestions for making it less flaky are welcome.")
    58  
    59  	// Recursive function that does defer/recover/repanic.
    60  	var f func(int)
    61  	f = func(n int) {
    62  		if n == 0 {
    63  			panic("done")
    64  		}
    65  		defer func() {
    66  			err := recover()
    67  			panic(err)
    68  		}()
    69  		f(n - 1)
    70  	}
    71  
    72  	time := func(f func()) time.Duration {
    73  		var times []time.Duration
    74  		for range 10 {
    75  			start := time.Now()
    76  			f()
    77  			times = append(times, time.Since(start))
    78  		}
    79  		slices.Sort(times)
    80  		times = times[1 : len(times)-1] // skip high and low, to reduce noise
    81  		var avg time.Duration
    82  		for _, v := range times {
    83  			avg += v / time.Duration(len(times))
    84  		}
    85  		return avg
    86  	}
    87  
    88  	a := time(func() {
    89  		defer func() { recover() }()
    90  		f(1024)
    91  	})
    92  	b := time(func() {
    93  		defer func() { recover() }()
    94  		f(2048)
    95  	})
    96  	m := b.Seconds() / a.Seconds()
    97  	t.Logf("a: %v, b: %v, m: %v", a, b, m)
    98  	if m > 3.5 {
    99  		t.Errorf("more than 2x time increase: %v", m)
   100  	}
   101  }
   102  
   103  // Test that panics with nil arguments produce the prefix
   104  // "runtime error:" per https://golang.org/issues/63813.
   105  func TestPanicNilErrorPrefix(t *testing.T) {
   106  	tests := []struct {
   107  		name      string
   108  		wantPanic string
   109  		fn        func()
   110  	}{
   111  		{
   112  			name:      "panic(nil)",
   113  			wantPanic: "runtime error: panic with nil argument",
   114  			fn: func() {
   115  				panic(nil)
   116  			},
   117  		},
   118  		{
   119  			name:      "panic((any)(nil))",
   120  			wantPanic: "runtime error: panic with nil argument",
   121  			fn: func() {
   122  				var foo any = nil
   123  				panic(foo)
   124  			},
   125  		},
   126  		{
   127  			name:      "panic((error)(nil))",
   128  			wantPanic: "runtime error: panic with nil argument",
   129  			fn: func() {
   130  				var err error
   131  				panic(err)
   132  			},
   133  		},
   134  	}
   135  
   136  	for _, tt := range tests {
   137  		tt := tt
   138  		t.Run(tt.name, func(t *testing.T) {
   139  			defer func() {
   140  				r := recover()
   141  				if r == nil {
   142  					t.Fatal("expected a panic")
   143  				}
   144  
   145  				re, ok := r.(runtime.Error)
   146  				if !ok {
   147  					t.Fatalf("wrong panic type: got %T, want runtime.Error", r)
   148  				}
   149  				if !strings.Contains(re.Error(), "runtime error: panic called with nil argument") {
   150  					t.Fatalf("mismatched message, missing `runtime error: panic with nil`, : got:\n%s", re.Error())
   151  				}
   152  			}()
   153  
   154  			tt.fn()
   155  		})
   156  	}
   157  }
   158  

View as plain text