github.com/Aoi-hosizora/ahlib@v1.5.1-0.20230404072829-241b93cf91c7/xtesting/xtesting_helper.go (about)

     1  package xtesting
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/exec"
     7  	"path"
     8  	"path/filepath"
     9  	"runtime"
    10  	"sync/atomic"
    11  	"testing"
    12  	_ "unsafe"
    13  )
    14  
    15  // ================
    16  // failTest related
    17  // ================
    18  
    19  // failTest outputs the error message and fails the test.
    20  func failTest(t testing.TB, skip int, failureMessage string, msgAndArgs ...interface{}) bool {
    21  	if skip < 0 {
    22  		skip = 0
    23  	}
    24  	extraSkip := int(atomic.LoadInt32(&_extraSkip))
    25  	skip = skip + extraSkip
    26  
    27  	_, file, line, _ := runtime.Caller(skip + 1)
    28  	message := fmt.Sprintf("%s:%d %s", path.Base(file), line, failureMessage)
    29  	_, _ = fmt.Fprintf(os.Stderr, "%s%s\n", message, combineMsgAndArgs(msgAndArgs...)) // use fmt.Fprintf() rather than t.Log() or t.Errorf()
    30  
    31  	failNow := atomic.LoadInt32(&_useFailNow) == 1
    32  	if !failNow {
    33  		t.Fail()
    34  	} else {
    35  		t.FailNow()
    36  	}
    37  	return false
    38  }
    39  
    40  var (
    41  	// _extraSkip is the extra skip. Note that this value cannot be less than zero, and it defaults to zero.
    42  	_extraSkip int32 = 0
    43  
    44  	// _useFailNow is a flag for using `FailNow` (if set to 1) rather than `Fail` (if set to 0), defaults to 0.
    45  	_useFailNow int32 = 0
    46  )
    47  
    48  // SetExtraSkip sets extra skip for testing functions. Note that this will be used when printing the failed message, and it defaults to zero.
    49  func SetExtraSkip(skip int32) {
    50  	if skip >= 0 {
    51  		atomic.StoreInt32(&_extraSkip, skip)
    52  	}
    53  }
    54  
    55  // UseFailNow makes testing functions use `FailNow` when tests failed, defaults to false, and it means to use `Fail` rather than `FailNow`.
    56  func UseFailNow(failNow bool) {
    57  	if failNow {
    58  		atomic.StoreInt32(&_useFailNow, 1)
    59  	} else {
    60  		atomic.StoreInt32(&_useFailNow, 0)
    61  	}
    62  }
    63  
    64  // combineMsgAndArgs generates message from given arguments.
    65  func combineMsgAndArgs(msgAndArgs ...interface{}) string {
    66  	if len(msgAndArgs) == 0 {
    67  		return ""
    68  	}
    69  
    70  	if len(msgAndArgs) == 1 {
    71  		msg := msgAndArgs[0]
    72  		if msgAsStr, ok := msg.(string); ok {
    73  			return msgAsStr
    74  		}
    75  		return fmt.Sprintf("%+v", msg)
    76  	}
    77  
    78  	return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...)
    79  }
    80  
    81  // =====================
    82  // mass helper functions
    83  // =====================
    84  
    85  // Assert panics when condition is false.
    86  func Assert(condition bool, format string, v ...interface{}) bool {
    87  	if !condition {
    88  		panic(fmt.Sprintf(format, v...))
    89  	}
    90  
    91  	return true
    92  }
    93  
    94  var _testGoToolFlag atomic.Value
    95  
    96  // GoCommand reports the path to the Go executable file, if the bin file is not available, GoCommand returns error. For more details,
    97  // please read the source code of src/internal/testenv/testenv.go.
    98  //
    99  // Example:
   100  // 	func TestXXX(t *testing.T) {
   101  // 		gocmd, err := GoCommand()
   102  // 		if err != nil {
   103  // 			t.Fail()
   104  // 		}
   105  // 		tmpdir := t.TempDir()
   106  //
   107  // 		modFile := path.Join(tmpdir, "go.mod")
   108  // 		if ioutil.WriteFile(modFile, []byte("module xxx\ngo 1.18"), 0666) != nil {
   109  // 			t.Fail()
   110  // 		}
   111  // 		sourceFile := path.Join(tmpdir, "test.go")
   112  // 		if ioutil.WriteFile(sourceFile, []byte("package main\nfunc main() { ... }"), 0666) != nil {
   113  // 			t.Fail()
   114  // 		}
   115  //
   116  // 		buildCmd := exec.Command(gocmd, "build", "-o", "test", sourceFile)
   117  // 		buildCmd.Dir = tmpdir
   118  // 		buildOut, err := buildCmd.CombinedOutput()
   119  // 		// ...
   120  //
   121  // 		runCmd := exec.Command("test")
   122  // 		buildCmd.Dir = tmpdir
   123  // 		runOut, err := runCmd.CombinedOutput()
   124  // 		// ...
   125  // 	}
   126  func GoCommand() (string, error) {
   127  	p := filepath.Join(runtime.GOROOT(), "bin", "go")
   128  	if _testGoToolFlag.Load() == true {
   129  		// enter only when testing GoCommand function
   130  		p += "_fake"
   131  	}
   132  
   133  	goBin, err := exec.LookPath(p)
   134  	if err != nil {
   135  		return "", fmt.Errorf("xtesting: go command is not found: %w", err)
   136  	}
   137  	return goBin, nil
   138  }