Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

skip: deprecate skip.If and replace it with assert.SkipIf #232

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ module paths pin to version `v2.3.0`.
execute binaries and test the output
* [poll](http://pkg.go.dev/gotest.tools/v3/poll) -
test asynchronous code by polling until a desired state is reached
* [skip](http://pkg.go.dev/gotest.tools/v3/skip) -
skip a test and print the source code of the condition used to skip the test

## Related

Expand Down
8 changes: 7 additions & 1 deletion assert/assert.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ import (
"gotest.tools/v3/internal/assert"
)

// BoolOrComparison can be a bool, or cmp.Comparison. See Assert() for usage.
// BoolOrComparison can be a bool, cmp.Comparison, or error. See Assert for
// details about how this type is used.
type BoolOrComparison interface{}

// TestingT is the subset of testing.T used by the assert package.
Expand Down Expand Up @@ -120,6 +121,11 @@ type helperT interface {
// A nil value is considered success, and a non-nil error is a failure.
// The return value of error.Error is used as the failure message.
//
//
// Extra details can be added to the failure message using msgAndArgs. msgAndArgs
// may be either a single string, or a format string and args that will be
// passed to fmt.Sprintf.
//
// Assert uses t.FailNow to fail the test. Like t.FailNow, Assert must be called
// from the goroutine running the test function, not from other
// goroutines created during the test. Use Check from other goroutines.
Expand Down
104 changes: 104 additions & 0 deletions assert/skip.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package assert

import (
"fmt"
"path"
"reflect"
"runtime"
"strings"

"gotest.tools/v3/internal/format"
"gotest.tools/v3/internal/source"
)

// SkipT is the interface accepted by SkipIf to skip tests. It is implemented by
// testing.T, and testing.B.
type SkipT interface {
Skip(args ...interface{})
Log(args ...interface{})
}

// SkipResult may be returned by a function used with SkipIf to provide a
// detailed message to use as part of the skip message.
type SkipResult interface {
Skip() bool
Message() string
}

// BoolOrCheckFunc can be a bool, func() bool, or func() SkipResult. Other
// types will panic. See SkipIf for details about how this type is used.
type BoolOrCheckFunc interface{}

// SkipIf skips the test if the condition evaluates to true. If the condition
// evaluates to false then SkipIf does nothing. SkipIf is a convenient way of
// skipping tests and using the literal source of the condition as the text of
// the skip message.
//
// For example, this usage would produce the following skip message:
//
// assert.SkipIf(t, runtime.GOOS == "windows", "not supported")
// // filename.go:11: runtime.GOOS == "windows": not supported
//
// The condition argument may be one of the following:
//
// bool
// The test will be skipped if the value is true. The literal source of the
// expression passed to SkipIf will be used as the skip message.
//
// func() bool
// The test will be skipped if the function returns true. The name of the
// function will be used as the skip message.
//
// func() SkipResult
// The test will be skipped if SkipResult.Skip return true. Both the name
// of the function and the return value of SkipResult.Message will be used
// as the skip message.
//
// Extra details can be added to the skip message using msgAndArgs. msgAndArgs
// may be either a single string, or a format string and args that will be
// passed to fmt.Sprintf.
func SkipIf(t SkipT, condition BoolOrCheckFunc, msgAndArgs ...interface{}) {
if ht, ok := t.(helperT); ok {
ht.Helper()
}
switch check := condition.(type) {
case bool:
ifCondition(t, check, msgAndArgs...)
case func() bool:
if check() {
t.Skip(format.WithCustomMessage(getFunctionName(check), msgAndArgs...))
}
case func() SkipResult:
result := check()
if result.Skip() {
msg := getFunctionName(check) + ": " + result.Message()
t.Skip(format.WithCustomMessage(msg, msgAndArgs...))
}
default:
panic(fmt.Sprintf("invalid type for condition arg: %T", check))
}
}

func getFunctionName(function interface{}) string {
funcPath := runtime.FuncForPC(reflect.ValueOf(function).Pointer()).Name()
return strings.SplitN(path.Base(funcPath), ".", 2)[1]
}

func ifCondition(t SkipT, condition bool, msgAndArgs ...interface{}) {
if ht, ok := t.(helperT); ok {
ht.Helper()
}
if !condition {
return
}
const (
stackIndex = 2
argPos = 1
)
source, err := source.FormattedCallExprArg(stackIndex, argPos)
if err != nil {
t.Log(err.Error())
t.Skip(format.Message(msgAndArgs...))
}
t.Skip(format.WithCustomMessage(source, msgAndArgs...))
}
39 changes: 39 additions & 0 deletions assert/skip_example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package assert_test

import (
"gotest.tools/v3/assert"
)

var apiVersion = ""

type env struct{}

func (e env) hasFeature(_ string) bool { return false }

var testEnv = env{}

func MissingFeature() bool { return false }

func ExampleSkipIf() {
assert.SkipIf(t, MissingFeature)
// --- SKIP: TestName (0.00s)
// skip.go:18: MissingFeature

assert.SkipIf(t, MissingFeature, "coming soon")
// --- SKIP: TestName (0.00s)
// skip.go:22: MissingFeature: coming soon
}

func ExampleSkipIf_withExpression() {
assert.SkipIf(t, apiVersion < version("v1.24"))
// --- SKIP: TestName (0.00s)
// skip.go:28: apiVersion < version("v1.24")

assert.SkipIf(t, !testEnv.hasFeature("build"), "coming soon")
// --- SKIP: TestName (0.00s)
// skip.go:32: !textenv.hasFeature("build"): coming soon
}

func version(v string) string {
return v
}
135 changes: 135 additions & 0 deletions assert/skip_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package assert

import (
"bytes"
"fmt"
"testing"

"gotest.tools/v3/assert/cmp"
)

type fakeSkipT struct {
reason string
logs []string
}

func (f *fakeSkipT) Skip(args ...interface{}) {
buf := new(bytes.Buffer)
for _, arg := range args {
buf.WriteString(fmt.Sprintf("%s", arg))
}
f.reason = buf.String()
}

func (f *fakeSkipT) Log(args ...interface{}) {
f.logs = append(f.logs, fmt.Sprintf("%s", args[0]))
}

func (f *fakeSkipT) Helper() {}

func version(v string) string {
return v
}

func TestSkipIFCondition(t *testing.T) {
skipT := &fakeSkipT{}
apiVersion := "v1.4"
SkipIf(skipT, apiVersion < version("v1.6"))

Equal(t, `apiVersion < version("v1.6")`, skipT.reason)
Assert(t, cmp.Len(skipT.logs, 0))
}

func TestSkipIfConditionWithMessage(t *testing.T) {
skipT := &fakeSkipT{}
apiVersion := "v1.4"
SkipIf(skipT, apiVersion < "v1.6", "see notes")

Equal(t, `apiVersion < "v1.6": see notes`, skipT.reason)
Assert(t, cmp.Len(skipT.logs, 0))
}

func TestSkipIfConditionMultiline(t *testing.T) {
skipT := &fakeSkipT{}
apiVersion := "v1.4"
SkipIf(
skipT,
apiVersion < "v1.6")

Equal(t, `apiVersion < "v1.6"`, skipT.reason)
Assert(t, cmp.Len(skipT.logs, 0))
}

func TestSkipIfConditionMultilineWithMessage(t *testing.T) {
skipT := &fakeSkipT{}
apiVersion := "v1.4"
SkipIf(
skipT,
apiVersion < "v1.6",
"see notes")

Equal(t, `apiVersion < "v1.6": see notes`, skipT.reason)
Assert(t, cmp.Len(skipT.logs, 0))
}

func TestSkipIfConditionNoSkip(t *testing.T) {
skipT := &fakeSkipT{}
SkipIf(skipT, false)

Equal(t, "", skipT.reason)
Assert(t, cmp.Len(skipT.logs, 0))
}

func SkipBecauseISaidSo() bool {
return true
}

func TestSkipIf(t *testing.T) {
skipT := &fakeSkipT{}
SkipIf(skipT, SkipBecauseISaidSo)

Equal(t, "SkipBecauseISaidSo", skipT.reason)
}

func TestSkipIfWithMessage(t *testing.T) {
skipT := &fakeSkipT{}
SkipIf(skipT, SkipBecauseISaidSo, "see notes")

Equal(t, "SkipBecauseISaidSo: see notes", skipT.reason)
}

func TestSkipIf_InvalidCondition(t *testing.T) {
skipT := &fakeSkipT{}
Assert(t, cmp.Panics(func() {
SkipIf(skipT, "just a string")
}))
}

func TestSkipIfWithSkipResultFunc(t *testing.T) {
t.Run("no extra message", func(t *testing.T) {
skipT := &fakeSkipT{}
SkipIf(skipT, alwaysSkipWithMessage)

Equal(t, "alwaysSkipWithMessage: skip because I said so!", skipT.reason)
})
t.Run("with extra message", func(t *testing.T) {
skipT := &fakeSkipT{}
SkipIf(skipT, alwaysSkipWithMessage, "also %v", 4)

Equal(t, "alwaysSkipWithMessage: skip because I said so!: also 4", skipT.reason)
})
}

func alwaysSkipWithMessage() SkipResult {
return skipResult{}
}

type skipResult struct{}

func (s skipResult) Skip() bool {
return true
}

func (s skipResult) Message() string {
return "skip because I said so!"
}
11 changes: 5 additions & 6 deletions env/env_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"gotest.tools/v3/assert"
"gotest.tools/v3/fs"
"gotest.tools/v3/internal/source"
"gotest.tools/v3/skip"
)

func TestPatchFromUnset(t *testing.T) {
Expand All @@ -23,7 +22,7 @@ func TestPatchFromUnset(t *testing.T) {
}

func TestPatch(t *testing.T) {
skip.If(t, os.Getenv("PATH") == "")
assert.SkipIf(t, os.Getenv("PATH") == "")
oldVal := os.Getenv("PATH")

key, value := "PATH", "NEWVALUE"
Expand All @@ -35,7 +34,7 @@ func TestPatch(t *testing.T) {
}

func TestPatch_IntegrationWithCleanup(t *testing.T) {
skip.If(t, source.GoVersionLessThan(1, 14))
assert.SkipIf(t, source.GoVersionLessThan(1, 14))

key := "totally_unique_env_var_key"
t.Run("cleanup in subtest", func(t *testing.T) {
Expand Down Expand Up @@ -67,7 +66,7 @@ func TestPatchAll(t *testing.T) {
}

func TestPatchAllWindows(t *testing.T) {
skip.If(t, runtime.GOOS != "windows")
assert.SkipIf(t, runtime.GOOS != "windows")
oldEnv := os.Environ()
newEnv := map[string]string{
"FIRST": "STARS",
Expand All @@ -92,7 +91,7 @@ func sorted(source []string) []string {
}

func TestPatchAll_IntegrationWithCleanup(t *testing.T) {
skip.If(t, source.GoVersionLessThan(1, 14))
assert.SkipIf(t, source.GoVersionLessThan(1, 14))

key := "totally_unique_env_var_key"
t.Run("cleanup in subtest", func(t *testing.T) {
Expand Down Expand Up @@ -145,7 +144,7 @@ func TestChangeWorkingDir(t *testing.T) {
}

func TestChangeWorkingDir_IntegrationWithCleanup(t *testing.T) {
skip.If(t, source.GoVersionLessThan(1, 14))
assert.SkipIf(t, source.GoVersionLessThan(1, 14))

tmpDir := fs.NewDir(t, t.Name())
defer tmpDir.Remove()
Expand Down
5 changes: 2 additions & 3 deletions fs/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"gotest.tools/v3/assert"
"gotest.tools/v3/fs"
"gotest.tools/v3/internal/source"
"gotest.tools/v3/skip"
)

func TestNewDirWithOpsAndManifestEqual(t *testing.T) {
Expand Down Expand Up @@ -67,7 +66,7 @@ func TestNewFile(t *testing.T) {
}

func TestNewFile_IntegrationWithCleanup(t *testing.T) {
skip.If(t, source.GoVersionLessThan(1, 14))
assert.SkipIf(t, source.GoVersionLessThan(1, 14))
var tmpFile *fs.File
t.Run("cleanup in subtest", func(t *testing.T) {
tmpFile = fs.NewFile(t, t.Name())
Expand All @@ -82,7 +81,7 @@ func TestNewFile_IntegrationWithCleanup(t *testing.T) {
}

func TestNewDir_IntegrationWithCleanup(t *testing.T) {
skip.If(t, source.GoVersionLessThan(1, 14))
assert.SkipIf(t, source.GoVersionLessThan(1, 14))
var tmpFile *fs.Dir
t.Run("cleanup in subtest", func(t *testing.T) {
tmpFile = fs.NewDir(t, t.Name())
Expand Down
Loading