blob: 54f0477d6acdc1ff2d720edff6e9645b8ea35ba7 [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package assert
import (
"fmt"
"reflect"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"google.golang.org/protobuf/testing/protocmp"
)
// NoErr fails the test fatally if err is not nil.
func NoErr(t *testing.T, err error) {
t.Helper()
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
}
// ErrLike fails the test fatally if err is nil or does not contain the
// expected substring.
func ErrLike(t *testing.T, err error, substr string) {
t.Helper()
if err == nil {
t.Fatalf("Expected error containing %q, got nil", substr)
}
if !strings.Contains(err.Error(), substr) {
t.Fatalf("Expected error containing %q, got: %v", substr, err)
}
}
// Equal fails the test if got != want (using simple comparison).
func Equal[T comparable](t *testing.T, want, got T) {
t.Helper()
if got != want {
t.Errorf("Mismatch:\nwant: %v\ngot: %v", want, got)
}
}
// Match fails the test if got and want do not match.
// It uses go-cmp and automatically handles proto messages correctly.
// Additional cmp.Options can be passed (e.g., protocmp.IgnoreUnknown()).
func Match(t *testing.T, want, got any, opts ...cmp.Option) {
t.Helper()
allOpts := append([]cmp.Option{protocmp.Transform()}, opts...)
if diff := cmp.Diff(want, got, allOpts...); diff != "" {
t.Errorf("Mismatch (-want +got):\n%s", diff)
}
}
// True fails the test if val is false.
func True(t *testing.T, val bool) {
t.Helper()
if !val {
t.Error("Expected true, got false")
}
}
// False fails the test if val is true.
func False(t *testing.T, val bool) {
t.Helper()
if val {
t.Error("Expected false, got true")
}
}
// Nil fails the test if val is not nil.
func Nil(t *testing.T, val any) {
t.Helper()
if !isNil(val) {
t.Errorf("Expected nil, got: %v", val)
}
}
// Len fails the test if the length of got is not want.
func Len[T any](t *testing.T, got []T, want int) {
t.Helper()
if len(got) != want {
t.Errorf("Expected length %d, got %d: %v", want, len(got), got)
}
}
// Empty fails the test if got is not empty.
func Empty[T any](t *testing.T, got []T) {
t.Helper()
if len(got) != 0 {
t.Errorf("Expected empty, got length %d: %v", len(got), got)
}
}
// PanicLike fails the test if f does not panic or if the panic value
// (formatted as a string) does not contain substr.
func PanicLike(t *testing.T, f func(), substr string) {
t.Helper()
defer func() {
r := recover()
if r == nil {
t.Fatalf("Expected panic containing %q, got none", substr)
}
rStr := fmt.Sprint(r)
if !strings.Contains(rStr, substr) {
t.Fatalf("Expected panic containing %q, got: %v", substr, r)
}
}()
f()
}
func isNil(i any) bool {
if i == nil {
return true
}
v := reflect.ValueOf(i)
switch v.Kind() {
case reflect.Chan, reflect.Func, reflect.Map, reflect.Pointer,
reflect.UnsafePointer, reflect.Interface, reflect.Slice:
return v.IsNil()
}
return false
}