github.com/mitranim/gg@v0.1.17/gtest/gtest_internal.go (about)

     1  package gtest
     2  
     3  import (
     4  	"fmt"
     5  	r "reflect"
     6  	"strings"
     7  
     8  	"github.com/mitranim/gg"
     9  	"github.com/mitranim/gg/grepr"
    10  )
    11  
    12  // Suboptimal, TODO revise.
    13  func reindent(src string) string {
    14  	return gg.JoinLines(gg.Map(gg.SplitLines(src), indent)...)
    15  }
    16  
    17  func indent(src string) string {
    18  	if src == `` {
    19  		return src
    20  	}
    21  	return gg.Indent + src
    22  }
    23  
    24  // TODO rename and make public.
    25  func goStringIndent[A any](val A) string { return grepr.StringIndent(val, 1) }
    26  
    27  func msgSingle[A any](val A) string {
    28  	if isSimple(val) {
    29  		return Msg(`value:`, gg.StringAny(val))
    30  	}
    31  
    32  	return gg.JoinLinesOpt(
    33  		Msg(`detailed:`, goStringIndent(val)),
    34  		Msg(`simple:`, gg.StringAny(val)),
    35  	)
    36  }
    37  
    38  func msgOpt(opt []any, src string) string {
    39  	return gg.JoinLinesOpt(
    40  		src,
    41  		MsgOpt(`extra:`, gg.SpacedOpt(opt...)),
    42  	)
    43  }
    44  
    45  func msgPanicNoneWithTest(fun func(), test func(error) bool) string {
    46  	return gg.JoinLinesOpt(msgNotPanic(), msgErrFunTest(fun, test))
    47  }
    48  
    49  func msgPanicNoneWithStr(fun func(), exp string) string {
    50  	return gg.JoinLinesOpt(msgNotPanic(), msgFun(fun), msgExp(exp))
    51  }
    52  
    53  func msgPanicNoneWithErr(fun func(), exp error) string {
    54  	return gg.JoinLinesOpt(msgNotPanic(), msgFun(fun), msgExp(exp))
    55  }
    56  
    57  func msgErrFunTest(fun func(), test func(error) bool) string {
    58  	return gg.JoinLinesOpt(msgFun(fun), msgErrTest(test))
    59  }
    60  
    61  func msgNotPanic() string { return `unexpected lack of panic` }
    62  
    63  func msgFun(val func()) string {
    64  	if val == nil {
    65  		return ``
    66  	}
    67  	return Msg(`function:`, gg.FuncName(val))
    68  }
    69  
    70  func msgErrTest(val func(error) bool) string {
    71  	if val == nil {
    72  		return ``
    73  	}
    74  	return Msg(`error test:`, gg.FuncName(val))
    75  }
    76  
    77  func msgErrMismatch(fun func(), test func(error) bool, err error) string {
    78  	return gg.JoinLinesOpt(
    79  		`unexpected error mismatch`,
    80  		msgErrFunTest(fun, test),
    81  		msgErrActual(err),
    82  	)
    83  }
    84  
    85  func msgErrMsgMismatch(fun func(), exp, act string) string {
    86  	return gg.JoinLinesOpt(
    87  		`unexpected error message mismatch`,
    88  		msgFun(fun),
    89  		Msg(`actual error message:`, act),
    90  		Msg(`expected error message substring:`, exp),
    91  	)
    92  }
    93  
    94  func msgErrIsMismatch(err, exp error) string {
    95  	return gg.JoinLinesOpt(
    96  		`unexpected error mismatch`,
    97  		msgErrActual(err),
    98  		Msg(`expected error via errors.Is:`, gg.StringAny(exp)),
    99  	)
   100  }
   101  
   102  func msgExp[A any](val A) string { return Msg(`expected:`, gg.StringAny(val)) }
   103  
   104  func msgErrorNone(test func(error) bool) string {
   105  	return gg.JoinLinesOpt(`unexpected lack of error`, msgErrTest(test))
   106  }
   107  
   108  func msgFunErr(fun func(), err error) string {
   109  	return gg.JoinLinesOpt(msgFun(fun), msgErr(err))
   110  }
   111  
   112  func msgErr(err error) string {
   113  	return gg.JoinLinesOpt(
   114  		Msg(`error trace:`, errTrace(err)),
   115  		Msg(`error string:`, gg.StringAny(err)),
   116  	)
   117  }
   118  
   119  func msgErrActual(err error) string {
   120  	return gg.JoinLinesOpt(
   121  		Msg(`actual error trace:`, errTrace(err)),
   122  		Msg(`actual error string:`, gg.StringAny(err)),
   123  	)
   124  }
   125  
   126  func errTrace(err error) string {
   127  	return strings.TrimSpace(gg.ErrTrace(err).StringIndent(1))
   128  }
   129  
   130  func msgSliceElemMissing[A ~[]B, B any](src A, val B) string {
   131  	return gg.JoinLinesOpt(`missing element in slice`, msgSliceElem(src, val))
   132  }
   133  
   134  func msgSliceElemUnexpected[A ~[]B, B any](src A, val B) string {
   135  	return gg.JoinLinesOpt(`unexpected element in slice`, msgSliceElem(src, val))
   136  }
   137  
   138  func msgSliceElem[A ~[]B, B any](src A, val B) string {
   139  	// TODO avoid detailed view when it's unnecessary.
   140  	return gg.JoinLinesOpt(
   141  		Msg(`slice detailed:`, goStringIndent(src)),
   142  		Msg(`element detailed:`, goStringIndent(val)),
   143  		Msg(`slice simple:`, gg.StringAny(src)),
   144  		Msg(`element simple:`, gg.StringAny(val)),
   145  	)
   146  }
   147  
   148  func msgLess[A any](one, two A) string {
   149  	return gg.JoinLinesOpt(`expected A < B`, msgAB(one, two))
   150  }
   151  
   152  func msgLessEq[A any](one, two A) string {
   153  	return gg.JoinLinesOpt(`expected A <= B`, msgAB(one, two))
   154  }
   155  
   156  func msgAB[A any](one, two A) string {
   157  	return gg.JoinLinesOpt(
   158  		// TODO avoid detailed view when it's unnecessary.
   159  		Msg(`A detailed:`, goStringIndent(one)),
   160  		Msg(`B detailed:`, goStringIndent(two)),
   161  		Msg(`A simple:`, gg.StringAny(one)),
   162  		Msg(`B simple:`, gg.StringAny(two)),
   163  	)
   164  }
   165  
   166  /*
   167  Should return `true` when stringifying the given value via `fmt.Sprint` produces
   168  basically the same representation as pretty-printing it via `grepr`, with no
   169  significant difference in information. We "discount" the string quotes in this
   170  case. TODO rename and move to `grepr`. This test for `fmt.Stringer` but ignores
   171  other text-encoding interfaces such as `gg.Appender` or `encoding.TextMarshaler`
   172  because `gtest` produces the "simple" representation by calling `fmt.Sprint`,
   173  which does not support any of those additional interfaces.
   174  */
   175  func isSimple(src any) bool {
   176  	return src == nil || (!gg.AnyIs[fmt.Stringer](src) &&
   177  		!gg.AnyIs[fmt.GoStringer](src) &&
   178  		isPrim(src))
   179  }
   180  
   181  // TODO should probably move to `gg` and make public.
   182  func isPrim(src any) bool {
   183  	val := r.ValueOf(src)
   184  
   185  	switch val.Kind() {
   186  	case r.Bool,
   187  		r.Int8, r.Int16, r.Int32, r.Int64, r.Int,
   188  		r.Uint8, r.Uint16, r.Uint32, r.Uint64, r.Uint, r.Uintptr,
   189  		r.Float32, r.Float64,
   190  		r.Complex64, r.Complex128,
   191  		r.String:
   192  		return true
   193  	default:
   194  		return false
   195  	}
   196  }