github.com/tetratelabs/wazero@v1.2.1/internal/testing/require/require.go (about)

     1  // Package require includes test assertions that fail the test immediately. This is like to testify, but without a
     2  // dependency.
     3  //
     4  // Note: Assertions here are internal and are free to be customized to only support valid WebAssembly types, or to
     5  // reduce code in tests that only require certain types.
     6  package require
     7  
     8  import (
     9  	"bytes"
    10  	"errors"
    11  	"fmt"
    12  	"path"
    13  	"reflect"
    14  	"runtime"
    15  	"strings"
    16  	"syscall"
    17  	"unicode"
    18  	"unicode/utf8"
    19  )
    20  
    21  // TestingT is an interface wrapper of functions used in TestingT
    22  type TestingT interface {
    23  	Fatal(args ...interface{})
    24  }
    25  
    26  type EqualTo interface {
    27  	EqualTo(that interface{}) bool
    28  }
    29  
    30  // TODO: implement, test and document each function without using testify
    31  
    32  // Contains fails if `s` does not contain `substr` using strings.Contains.
    33  //
    34  //   - formatWithArgs are optional. When the first is a string that contains '%', it is treated like fmt.Sprintf.
    35  func Contains(t TestingT, s, substr string, formatWithArgs ...interface{}) {
    36  	if !strings.Contains(s, substr) {
    37  		fail(t, fmt.Sprintf("expected %q to contain %q", s, substr), "", formatWithArgs...)
    38  	}
    39  }
    40  
    41  // Equal fails if the actual value is not equal to the expected.
    42  //
    43  //   - formatWithArgs are optional. When the first is a string that contains '%', it is treated like fmt.Sprintf.
    44  func Equal(t TestingT, expected, actual interface{}, formatWithArgs ...interface{}) {
    45  	if expected == nil {
    46  		Nil(t, actual)
    47  		return
    48  	}
    49  	if equal(expected, actual) {
    50  		return
    51  	}
    52  	_, expectString := expected.(string)
    53  	if actual == nil {
    54  		if expectString {
    55  			fail(t, fmt.Sprintf("expected %q, but was nil", expected), "", formatWithArgs...)
    56  		} else {
    57  			fail(t, fmt.Sprintf("expected %#v, but was nil", expected), "", formatWithArgs...)
    58  		}
    59  		return
    60  	}
    61  
    62  	// Include the type name if the actual wasn't the same
    63  	et, at := reflect.ValueOf(expected).Type(), reflect.ValueOf(actual).Type()
    64  	if et != at {
    65  		if expectString {
    66  			fail(t, fmt.Sprintf("expected %q, but was %s(%v)", expected, at, actual), "", formatWithArgs...)
    67  		} else {
    68  			fail(t, fmt.Sprintf("expected %s(%v), but was %s(%v)", et, expected, at, actual), "", formatWithArgs...)
    69  		}
    70  		return
    71  	}
    72  
    73  	// Inline the comparison if the types are likely small:
    74  	if expectString {
    75  		// Don't use %q as it escapes newlines!
    76  		fail(t, fmt.Sprintf("expected \"%s\", but was \"%s\"", expected, actual), "", formatWithArgs...)
    77  		return
    78  	} else if et.Kind() < reflect.Array {
    79  		fail(t, fmt.Sprintf("expected %v, but was %v", expected, actual), "", formatWithArgs...)
    80  		return
    81  	} else if et.Kind() == reflect.Func {
    82  		// compare funcs by string pointer
    83  		expected := fmt.Sprintf("%v", expected)
    84  		actual := fmt.Sprintf("%v", actual)
    85  		if expected != actual {
    86  			fail(t, fmt.Sprintf("expected %s, but was %s", expected, actual), "", formatWithArgs...)
    87  		}
    88  		return
    89  	} else if eq, ok := actual.(EqualTo); ok {
    90  		if !eq.EqualTo(expected) {
    91  			fail(t, fmt.Sprintf("expected %v, but was %v", expected, actual), "", formatWithArgs...)
    92  		}
    93  	}
    94  
    95  	// If we have the same type, and it isn't a string, but the expected and actual values on a different line.
    96  	// This allows easier comparison without using a diff library.
    97  	fail(t, "unexpected value", fmt.Sprintf("expected:\n\t%#v\nwas:\n\t%#v\n", expected, actual), formatWithArgs...)
    98  }
    99  
   100  // equal speculatively tries to cast the inputs as byte arrays and falls back to reflection.
   101  func equal(expected, actual interface{}) bool {
   102  	if b1, ok := expected.([]byte); !ok {
   103  		return reflect.DeepEqual(expected, actual)
   104  	} else if b2, ok := actual.([]byte); ok {
   105  		return bytes.Equal(b1, b2)
   106  	}
   107  	return false
   108  }
   109  
   110  // EqualError fails if the error is nil or its `Error()` value is not equal to
   111  // the expected string.
   112  //
   113  //   - formatWithArgs are optional. When the first is a string that contains '%', it is treated like fmt.Sprintf.
   114  func EqualError(t TestingT, err error, expected string, formatWithArgs ...interface{}) {
   115  	if err == nil {
   116  		fail(t, "expected an error, but was nil", "", formatWithArgs...)
   117  		return
   118  	}
   119  	actual := err.Error()
   120  	if actual != expected {
   121  		fail(t, fmt.Sprintf("expected error \"%s\", but was \"%s\"", expected, actual), "", formatWithArgs...)
   122  	}
   123  }
   124  
   125  // Error fails if the err is nil.
   126  //
   127  //   - formatWithArgs are optional. When the first is a string that contains '%', it is treated like fmt.Sprintf.
   128  func Error(t TestingT, err error, formatWithArgs ...interface{}) {
   129  	if err == nil {
   130  		fail(t, "expected an error, but was nil", "", formatWithArgs...)
   131  	}
   132  }
   133  
   134  // EqualErrno should be used for functions that return syscall.Errno or nil.
   135  func EqualErrno(t TestingT, expected syscall.Errno, err error, formatWithArgs ...interface{}) {
   136  	if err == nil {
   137  		fail(t, "expected a syscall.Errno, but was nil", "", formatWithArgs...)
   138  		return
   139  	}
   140  	if se, ok := err.(syscall.Errno); !ok {
   141  		fail(t, fmt.Sprintf("expected %v to be a syscall.Errno", err), "", formatWithArgs...)
   142  	} else if se != expected {
   143  		fail(t, fmt.Sprintf("expected Errno %#[1]v(%[1]s), but was %#[2]v(%[2]s)", expected, err), "", formatWithArgs...)
   144  	}
   145  }
   146  
   147  // ErrorIs fails if the err is nil or errors.Is fails against the expected.
   148  //
   149  //   - formatWithArgs are optional. When the first is a string that contains '%', it is treated like fmt.Sprintf.
   150  func ErrorIs(t TestingT, err, target error, formatWithArgs ...interface{}) {
   151  	if err == nil {
   152  		fail(t, "expected an error, but was nil", "", formatWithArgs...)
   153  		return
   154  	}
   155  	if !errors.Is(err, target) {
   156  		fail(t, fmt.Sprintf("expected errors.Is(%v, %v), but it wasn't", err, target), "", formatWithArgs...)
   157  	}
   158  }
   159  
   160  // False fails if the actual value was true.
   161  //
   162  //   - formatWithArgs are optional. When the first is a string that contains '%', it is treated like fmt.Sprintf.
   163  func False(t TestingT, actual bool, formatWithArgs ...interface{}) {
   164  	if actual {
   165  		fail(t, "expected false, but was true", "", formatWithArgs...)
   166  	}
   167  }
   168  
   169  // Nil fails if the object is not nil.
   170  //
   171  //   - formatWithArgs are optional. When the first is a string that contains '%', it is treated like fmt.Sprintf.
   172  func Nil(t TestingT, object interface{}, formatWithArgs ...interface{}) {
   173  	if !isNil(object) {
   174  		fail(t, fmt.Sprintf("expected nil, but was %v", object), "", formatWithArgs...)
   175  	}
   176  }
   177  
   178  // NoError fails if the err is not nil.
   179  //
   180  //   - formatWithArgs are optional. When the first is a string that contains '%', it is treated like fmt.Sprintf.
   181  func NoError(t TestingT, err error, formatWithArgs ...interface{}) {
   182  	if err != nil {
   183  		fail(t, fmt.Sprintf("expected no error, but was %v", err), "", formatWithArgs...)
   184  	}
   185  }
   186  
   187  // NotEqual fails if the actual value is equal to the expected.
   188  //
   189  //   - formatWithArgs are optional. When the first is a string that contains '%', it is treated like fmt.Sprintf.
   190  func NotEqual(t TestingT, expected, actual interface{}, formatWithArgs ...interface{}) {
   191  	if !equal(expected, actual) {
   192  		return
   193  	}
   194  	_, expectString := expected.(string)
   195  	if expectString {
   196  		fail(t, fmt.Sprintf("expected to not equal %q", actual), "", formatWithArgs...)
   197  		return
   198  	}
   199  	fail(t, fmt.Sprintf("expected to not equal %#v", actual), "", formatWithArgs...)
   200  }
   201  
   202  // NotNil fails if the object is nil.
   203  //
   204  //   - formatWithArgs are optional. When the first is a string that contains '%', it is treated like fmt.Sprintf.
   205  func NotNil(t TestingT, object interface{}, formatWithArgs ...interface{}) {
   206  	if isNil(object) {
   207  		fail(t, "expected to not be nil", "", formatWithArgs...)
   208  	}
   209  }
   210  
   211  // isNil is less efficient for the sake of less code vs tracking all the nil types in Go.
   212  func isNil(object interface{}) (isNil bool) {
   213  	if object == nil {
   214  		return true
   215  	}
   216  
   217  	v := reflect.ValueOf(object)
   218  
   219  	defer func() {
   220  		if recovered := recover(); recovered != nil {
   221  			// ignore problems using isNil on a type that can't be nil
   222  			isNil = false
   223  		}
   224  	}()
   225  
   226  	isNil = v.IsNil()
   227  	return
   228  }
   229  
   230  // NotSame fails if the inputs point to the same object.
   231  //
   232  //   - formatWithArgs are optional. When the first is a string that contains '%', it is treated like fmt.Sprintf.
   233  func NotSame(t TestingT, expected, actual interface{}, formatWithArgs ...interface{}) {
   234  	if equalsPointer(expected, actual) {
   235  		fail(t, fmt.Sprintf("expected %v to point to a different object", actual), "", formatWithArgs...)
   236  		return
   237  	}
   238  }
   239  
   240  // CapturePanic returns an error recovered from a panic. If the panic was not an error, this converts it to one.
   241  func CapturePanic(panics func()) (err error) {
   242  	defer func() {
   243  		if recovered := recover(); recovered != nil {
   244  			if e, ok := recovered.(error); ok {
   245  				err = e
   246  			} else {
   247  				err = fmt.Errorf("%v", recovered)
   248  			}
   249  		}
   250  	}()
   251  	panics()
   252  	return
   253  }
   254  
   255  // Same fails if the inputs don't point to the same object.
   256  //
   257  //   - formatWithArgs are optional. When the first is a string that contains '%', it is treated like fmt.Sprintf.
   258  func Same(t TestingT, expected, actual interface{}, formatWithArgs ...interface{}) {
   259  	if !equalsPointer(expected, actual) {
   260  		fail(t, fmt.Sprintf("expected %v to point to the same object as %v", actual, expected), "", formatWithArgs...)
   261  		return
   262  	}
   263  }
   264  
   265  func equalsPointer(expected, actual interface{}) bool {
   266  	expectedV := reflect.ValueOf(expected)
   267  	if expectedV.Kind() != reflect.Ptr {
   268  		panic("BUG: expected was not a pointer")
   269  	}
   270  	actualV := reflect.ValueOf(actual)
   271  	if actualV.Kind() != reflect.Ptr {
   272  		panic("BUG: actual was not a pointer")
   273  	}
   274  
   275  	if t1, t2 := reflect.TypeOf(expectedV), reflect.TypeOf(actualV); t1 != t2 {
   276  		return false
   277  	} else {
   278  		return expected == actual
   279  	}
   280  }
   281  
   282  // True fails if the actual value wasn't.
   283  //
   284  //   - formatWithArgs are optional. When the first is a string that contains '%', it is treated like fmt.Sprintf.
   285  func True(t TestingT, actual bool, formatWithArgs ...interface{}) {
   286  	if !actual {
   287  		fail(t, "expected true, but was false", "", formatWithArgs...)
   288  	}
   289  }
   290  
   291  // Zero fails if the actual value wasn't.
   292  //
   293  //   - formatWithArgs are optional. When the first is a string that contains '%', it is treated like fmt.Sprintf.
   294  //
   295  // Note: This isn't precise to numeric types, but we don't care as being more precise is more code and tests.
   296  func Zero(t TestingT, i interface{}, formatWithArgs ...interface{}) {
   297  	if i == nil {
   298  		fail(t, "expected zero, but was nil", "", formatWithArgs...)
   299  	}
   300  	zero := reflect.Zero(reflect.TypeOf(i))
   301  	if i != zero.Interface() {
   302  		fail(t, fmt.Sprintf("expected zero, but was %v", i), "", formatWithArgs...)
   303  	}
   304  }
   305  
   306  // fail tries to treat the formatWithArgs as fmt.Sprintf parameters or joins on space.
   307  func fail(t TestingT, m1, m2 string, formatWithArgs ...interface{}) {
   308  	var failure string
   309  	if len(formatWithArgs) > 0 {
   310  		if s, ok := formatWithArgs[0].(string); ok && strings.Contains(s, "%") {
   311  			failure = fmt.Sprintf(m1+": "+s, formatWithArgs[1:]...)
   312  		} else {
   313  			var builder strings.Builder
   314  			builder.WriteString(fmt.Sprintf("%s: %v", m1, formatWithArgs[0]))
   315  			for _, v := range formatWithArgs[1:] {
   316  				builder.WriteByte(' ')
   317  				builder.WriteString(fmt.Sprintf("%v", v))
   318  			}
   319  			failure = builder.String()
   320  		}
   321  	} else {
   322  		failure = m1
   323  	}
   324  	if m2 != "" {
   325  		failure = failure + "\n" + m2
   326  	}
   327  
   328  	// Don't write the failStack in our own package!
   329  	if fs := failStack(); len(fs) > 0 {
   330  		t.Fatal(failure + "\n" + strings.Join(fs, "\n"))
   331  	} else {
   332  		t.Fatal(failure)
   333  	}
   334  }
   335  
   336  // failStack returns the stack leading to the failure, without test infrastructure.
   337  //
   338  // Note: This is similar to assert.CallerInfo in testify
   339  // Note: This is untested because it is a lot of work to do that. The rationale to punt is this is a test-only internal
   340  // type which returns optional info. Someone can add tests, but they'd need to do that as an integration test in a
   341  // different package with something stable line-number-wise.
   342  func failStack() (fs []string) {
   343  	for i := 0; ; i++ {
   344  		pc, file, line, ok := runtime.Caller(i)
   345  		if !ok {
   346  			break // don't loop forever on a bug
   347  		}
   348  
   349  		f := runtime.FuncForPC(pc)
   350  		if f == nil {
   351  			break // don't loop forever on a bug
   352  		}
   353  		name := f.Name()
   354  
   355  		if name == "testing.tRunner" {
   356  			break // Don't add the runner from src/testing/testing.go
   357  		}
   358  
   359  		// Ensure we don't add functions in the require package to the failure stack.
   360  		dir := path.Dir(file)
   361  		if path.Base(dir) != "require" {
   362  			fs = append(fs, fmt.Sprintf("%s:%d", file, line))
   363  		}
   364  
   365  		// Stop the stack when we get to a test. Strip off any leading package name first!
   366  		if dot := strings.Index(name, "."); dot > 0 {
   367  			if isTest(name[dot+1:]) {
   368  				return
   369  			}
   370  		}
   371  	}
   372  	return
   373  }
   374  
   375  var testPrefixes = []string{"Test", "Benchmark", "Example"}
   376  
   377  // isTest is similar to load.isTest in Go's src/cmd/go/internal/load/test.go
   378  func isTest(name string) bool {
   379  	for _, prefix := range testPrefixes {
   380  		if !strings.HasPrefix(name, prefix) {
   381  			return false
   382  		}
   383  		if len(name) == len(prefix) { // "Test" is ok
   384  			return true
   385  		}
   386  		if r, _ := utf8.DecodeRuneInString(name[len(prefix):]); !unicode.IsLower(r) {
   387  			return true
   388  		}
   389  	}
   390  	return false
   391  }