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 }