github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/util/runtime/runtime_test.go (about)

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package runtime
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"io"
    23  	"net/http"
    24  	"os"
    25  	"regexp"
    26  	"strings"
    27  	"testing"
    28  )
    29  
    30  func TestHandleCrash(t *testing.T) {
    31  	defer func() {
    32  		if x := recover(); x == nil {
    33  			t.Errorf("Expected a panic to recover from")
    34  		}
    35  	}()
    36  	defer HandleCrash()
    37  	panic("Test Panic")
    38  }
    39  
    40  func TestCustomHandleCrash(t *testing.T) {
    41  	old := PanicHandlers
    42  	defer func() { PanicHandlers = old }()
    43  	var result interface{}
    44  	PanicHandlers = []func(interface{}){
    45  		func(r interface{}) {
    46  			result = r
    47  		},
    48  	}
    49  	func() {
    50  		defer func() {
    51  			if x := recover(); x == nil {
    52  				t.Errorf("Expected a panic to recover from")
    53  			}
    54  		}()
    55  		defer HandleCrash()
    56  		panic("test")
    57  	}()
    58  	if result != "test" {
    59  		t.Errorf("did not receive custom handler")
    60  	}
    61  }
    62  
    63  func TestCustomHandleError(t *testing.T) {
    64  	old := ErrorHandlers
    65  	defer func() { ErrorHandlers = old }()
    66  	var result error
    67  	ErrorHandlers = []func(error){
    68  		func(err error) {
    69  			result = err
    70  		},
    71  	}
    72  	err := fmt.Errorf("test")
    73  	HandleError(err)
    74  	if result != err {
    75  		t.Errorf("did not receive custom handler")
    76  	}
    77  }
    78  
    79  func TestHandleCrashLog(t *testing.T) {
    80  	log, err := captureStderr(func() {
    81  		defer func() {
    82  			if r := recover(); r == nil {
    83  				t.Fatalf("expected a panic to recover from")
    84  			}
    85  		}()
    86  		defer HandleCrash()
    87  		panic("test panic")
    88  	})
    89  	if err != nil {
    90  		t.Fatalf("%v", err)
    91  	}
    92  	// Example log:
    93  	//
    94  	// ...] Observed a panic: test panic
    95  	// goroutine 6 [running]:
    96  	// command-line-arguments.logPanic(0x..., 0x...)
    97  	// 	.../src/k8s.io/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/runtime/runtime.go:69 +0x...
    98  	lines := strings.Split(log, "\n")
    99  	if len(lines) < 4 {
   100  		t.Fatalf("panic log should have 1 line of message, 1 line per goroutine and 2 lines per function call")
   101  	}
   102  	if match, _ := regexp.MatchString("Observed a panic: test panic", lines[0]); !match {
   103  		t.Errorf("mismatch panic message: %s", lines[0])
   104  	}
   105  	// The following regexp's verify that Kubernetes panic log matches Golang stdlib
   106  	// stacktrace pattern. We need to update these regexp's if stdlib changes its pattern.
   107  	if match, _ := regexp.MatchString(`goroutine [0-9]+ \[.+\]:`, lines[1]); !match {
   108  		t.Errorf("mismatch goroutine: %s", lines[1])
   109  	}
   110  	if match, _ := regexp.MatchString(`logPanic(.*)`, lines[2]); !match {
   111  		t.Errorf("mismatch symbolized function name: %s", lines[2])
   112  	}
   113  	if match, _ := regexp.MatchString(`runtime\.go:[0-9]+ \+0x`, lines[3]); !match {
   114  		t.Errorf("mismatch file/line/offset information: %s", lines[3])
   115  	}
   116  }
   117  
   118  func TestHandleCrashLogSilenceHTTPErrAbortHandler(t *testing.T) {
   119  	log, err := captureStderr(func() {
   120  		defer func() {
   121  			if r := recover(); r != http.ErrAbortHandler {
   122  				t.Fatalf("expected to recover from http.ErrAbortHandler")
   123  			}
   124  		}()
   125  		defer HandleCrash()
   126  		panic(http.ErrAbortHandler)
   127  	})
   128  	if err != nil {
   129  		t.Fatalf("%v", err)
   130  	}
   131  	if len(log) > 0 {
   132  		t.Fatalf("expected no stderr log, got: %s", log)
   133  	}
   134  }
   135  
   136  // captureStderr redirects stderr to result string, and then restore stderr from backup
   137  func captureStderr(f func()) (string, error) {
   138  	r, w, err := os.Pipe()
   139  	if err != nil {
   140  		return "", err
   141  	}
   142  	bak := os.Stderr
   143  	os.Stderr = w
   144  	defer func() { os.Stderr = bak }()
   145  
   146  	resultCh := make(chan string)
   147  	// copy the output in a separate goroutine so printing can't block indefinitely
   148  	go func() {
   149  		var buf bytes.Buffer
   150  		io.Copy(&buf, r)
   151  		resultCh <- buf.String()
   152  	}()
   153  
   154  	f()
   155  	w.Close()
   156  
   157  	return <-resultCh, nil
   158  }