skip.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. /*
  2. Package skip provides functions for skipping a test and printing the source code
  3. of the condition used to skip the test.
  4. */
  5. package skip // import "gotest.tools/v3/skip"
  6. import (
  7. "fmt"
  8. "path"
  9. "reflect"
  10. "runtime"
  11. "strings"
  12. "gotest.tools/v3/internal/format"
  13. "gotest.tools/v3/internal/source"
  14. )
  15. type skipT interface {
  16. Skip(args ...interface{})
  17. Log(args ...interface{})
  18. }
  19. // Result of skip function
  20. type Result interface {
  21. Skip() bool
  22. Message() string
  23. }
  24. type helperT interface {
  25. Helper()
  26. }
  27. // BoolOrCheckFunc can be a bool, func() bool, or func() Result. Other types will panic
  28. type BoolOrCheckFunc interface{}
  29. // If the condition expression evaluates to true, skip the test.
  30. //
  31. // The condition argument may be one of three types: bool, func() bool, or
  32. // func() SkipResult.
  33. // When called with a bool, the test will be skip if the condition evaluates to true.
  34. // When called with a func() bool, the test will be skip if the function returns true.
  35. // When called with a func() Result, the test will be skip if the Skip method
  36. // of the result returns true.
  37. // The skip message will contain the source code of the expression.
  38. // Extra message text can be passed as a format string with args.
  39. func If(t skipT, condition BoolOrCheckFunc, msgAndArgs ...interface{}) {
  40. if ht, ok := t.(helperT); ok {
  41. ht.Helper()
  42. }
  43. switch check := condition.(type) {
  44. case bool:
  45. ifCondition(t, check, msgAndArgs...)
  46. case func() bool:
  47. if check() {
  48. t.Skip(format.WithCustomMessage(getFunctionName(check), msgAndArgs...))
  49. }
  50. case func() Result:
  51. result := check()
  52. if result.Skip() {
  53. msg := getFunctionName(check) + ": " + result.Message()
  54. t.Skip(format.WithCustomMessage(msg, msgAndArgs...))
  55. }
  56. default:
  57. panic(fmt.Sprintf("invalid type for condition arg: %T", check))
  58. }
  59. }
  60. func getFunctionName(function interface{}) string {
  61. funcPath := runtime.FuncForPC(reflect.ValueOf(function).Pointer()).Name()
  62. return strings.SplitN(path.Base(funcPath), ".", 2)[1]
  63. }
  64. func ifCondition(t skipT, condition bool, msgAndArgs ...interface{}) {
  65. if ht, ok := t.(helperT); ok {
  66. ht.Helper()
  67. }
  68. if !condition {
  69. return
  70. }
  71. const (
  72. stackIndex = 2
  73. argPos = 1
  74. )
  75. source, err := source.FormattedCallExprArg(stackIndex, argPos)
  76. if err != nil {
  77. t.Log(err.Error())
  78. t.Skip(format.Message(msgAndArgs...))
  79. }
  80. t.Skip(format.WithCustomMessage(source, msgAndArgs...))
  81. }