github.com/interconnectedcloud/qdr-operator@v0.0.0-20210826174505-576d2b33dac7/test/e2e/framework/ginkgowrapper/wrapper.go (about)

     1  /*
     2  Copyright 2019 The Interconnectedcloud 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 ginkgowrapper wraps Ginkgo Fail and Skip functions to panic
    18  // with structured data instead of a constant string.
    19  package ginkgowrapper
    20  
    21  import (
    22  	"bufio"
    23  	"bytes"
    24  	"regexp"
    25  	"runtime"
    26  	"runtime/debug"
    27  	"strings"
    28  
    29  	"github.com/onsi/ginkgo"
    30  )
    31  
    32  // FailurePanic is the value that will be panicked from Fail.
    33  type FailurePanic struct {
    34  	Message        string // The failure message passed to Fail
    35  	Filename       string // The filename that is the source of the failure
    36  	Line           int    // The line number of the filename that is the source of the failure
    37  	FullStackTrace string // A full stack trace starting at the source of the failure
    38  }
    39  
    40  // String makes FailurePanic look like the old Ginkgo panic when printed.
    41  func (FailurePanic) String() string { return ginkgo.GINKGO_PANIC }
    42  
    43  // Fail wraps ginkgo.Fail so that it panics with more useful
    44  // information about the failure. This function will panic with a
    45  // FailurePanic.
    46  func Fail(message string, callerSkip ...int) {
    47  	skip := 1
    48  	if len(callerSkip) > 0 {
    49  		skip += callerSkip[0]
    50  	}
    51  
    52  	_, file, line, _ := runtime.Caller(skip)
    53  	fp := FailurePanic{
    54  		Message:        message,
    55  		Filename:       file,
    56  		Line:           line,
    57  		FullStackTrace: pruneStack(skip),
    58  	}
    59  
    60  	defer func() {
    61  		e := recover()
    62  		if e != nil {
    63  			panic(fp)
    64  		}
    65  	}()
    66  
    67  	ginkgo.Fail(message, skip)
    68  }
    69  
    70  // SkipPanic is the value that will be panicked from Skip.
    71  type SkipPanic struct {
    72  	Message        string // The failure message passed to Fail
    73  	Filename       string // The filename that is the source of the failure
    74  	Line           int    // The line number of the filename that is the source of the failure
    75  	FullStackTrace string // A full stack trace starting at the source of the failure
    76  }
    77  
    78  // String makes SkipPanic look like the old Ginkgo panic when printed.
    79  func (SkipPanic) String() string { return ginkgo.GINKGO_PANIC }
    80  
    81  // Skip wraps ginkgo.Skip so that it panics with more useful
    82  // information about why the test is being skipped. This function will
    83  // panic with a SkipPanic.
    84  func Skip(message string, callerSkip ...int) {
    85  	skip := 1
    86  	if len(callerSkip) > 0 {
    87  		skip += callerSkip[0]
    88  	}
    89  
    90  	_, file, line, _ := runtime.Caller(skip)
    91  	sp := SkipPanic{
    92  		Message:        message,
    93  		Filename:       file,
    94  		Line:           line,
    95  		FullStackTrace: pruneStack(skip),
    96  	}
    97  
    98  	defer func() {
    99  		e := recover()
   100  		if e != nil {
   101  			panic(sp)
   102  		}
   103  	}()
   104  
   105  	ginkgo.Skip(message, skip)
   106  }
   107  
   108  // ginkgo adds a lot of test running infrastructure to the stack, so
   109  // we filter those out
   110  var stackSkipPattern = regexp.MustCompile(`onsi/ginkgo`)
   111  
   112  func pruneStack(skip int) string {
   113  	skip += 2 // one for pruneStack and one for debug.Stack
   114  	stack := debug.Stack()
   115  	scanner := bufio.NewScanner(bytes.NewBuffer(stack))
   116  	var prunedStack []string
   117  
   118  	// skip the top of the stack
   119  	for i := 0; i < 2*skip+1; i++ {
   120  		scanner.Scan()
   121  	}
   122  
   123  	for scanner.Scan() {
   124  		if stackSkipPattern.Match(scanner.Bytes()) {
   125  			scanner.Scan() // these come in pairs
   126  		} else {
   127  			prunedStack = append(prunedStack, scanner.Text())
   128  			scanner.Scan() // these come in pairs
   129  			prunedStack = append(prunedStack, scanner.Text())
   130  		}
   131  	}
   132  
   133  	return strings.Join(prunedStack, "\n")
   134  }