...

Source file src/k8s.io/kubernetes/test/e2e/framework/bugs.go

Documentation: k8s.io/kubernetes/test/e2e/framework

     1  /*
     2  Copyright 2023 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 framework
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"os"
    23  	"path/filepath"
    24  	"sort"
    25  	"strings"
    26  	"sync"
    27  
    28  	"github.com/onsi/ginkgo/v2/types"
    29  )
    30  
    31  var (
    32  	bugs     []Bug
    33  	bugMutex sync.Mutex
    34  )
    35  
    36  // RecordBug stores information about a bug in the E2E suite source code that
    37  // cannot be reported through ginkgo.Fail because it was found outside of some
    38  // test, for example during test registration.
    39  //
    40  // This can be used instead of raising a panic. Then all bugs can be reported
    41  // together instead of failing after the first one.
    42  func RecordBug(bug Bug) {
    43  	bugMutex.Lock()
    44  	defer bugMutex.Unlock()
    45  
    46  	bugs = append(bugs, bug)
    47  }
    48  
    49  type Bug struct {
    50  	FileName   string
    51  	LineNumber int
    52  	Message    string
    53  }
    54  
    55  // NewBug creates a new bug with a location that is obtained by skipping a certain number
    56  // of stack frames. Passing zero will record the source code location of the direct caller
    57  // of NewBug.
    58  func NewBug(message string, skip int) Bug {
    59  	location := types.NewCodeLocation(skip + 1)
    60  	return Bug{FileName: location.FileName, LineNumber: location.LineNumber, Message: message}
    61  }
    62  
    63  // FormatBugs produces a report that includes all bugs recorded earlier via
    64  // RecordBug. An error is returned with the report if there have been bugs.
    65  func FormatBugs() error {
    66  	bugMutex.Lock()
    67  	defer bugMutex.Unlock()
    68  
    69  	if len(bugs) == 0 {
    70  		return nil
    71  	}
    72  
    73  	lines := make([]string, 0, len(bugs))
    74  	wd, err := os.Getwd()
    75  	if err != nil {
    76  		return fmt.Errorf("get current directory: %v", err)
    77  	}
    78  	// Sort by file name, line number, message. For the sake of simplicity
    79  	// this uses the full file name even though the output the may use a
    80  	// relative path. Usually the result should be the same because full
    81  	// paths will all have the same prefix.
    82  	sort.Slice(bugs, func(i, j int) bool {
    83  		switch strings.Compare(bugs[i].FileName, bugs[j].FileName) {
    84  		case -1:
    85  			return true
    86  		case 1:
    87  			return false
    88  		}
    89  		if bugs[i].LineNumber < bugs[j].LineNumber {
    90  			return true
    91  		}
    92  		if bugs[i].LineNumber > bugs[j].LineNumber {
    93  			return false
    94  		}
    95  		return bugs[i].Message < bugs[j].Message
    96  	})
    97  	for _, bug := range bugs {
    98  		// Use relative paths, if possible.
    99  		path := bug.FileName
   100  		if wd != "" {
   101  			if relpath, err := filepath.Rel(wd, bug.FileName); err == nil {
   102  				path = relpath
   103  			}
   104  		}
   105  		lines = append(lines, fmt.Sprintf("ERROR: %s:%d: %s\n", path, bug.LineNumber, strings.TrimSpace(bug.Message)))
   106  	}
   107  	return errors.New(strings.Join(lines, ""))
   108  }
   109  

View as plain text