55 "fmt"
66 "math"
77 "strings"
8+ "unicode/utf8"
89)
910
1011const (
@@ -37,25 +38,35 @@ type failure[T any] struct {
3738// String implements [fmt.Stringer] for failure, allowing it to print itself in the test log.
3839func (f failure [T ]) String () string {
3940 s := & strings.Builder {}
40- s .WriteByte ('\n' )
41-
42- s .WriteString (f .cfg .title )
43- s .WriteByte ('\n' )
44- s .WriteString (strings .Repeat ("-" , len (f .cfg .title )))
45- s .WriteString ("\n \n " )
41+ f .cfg .writeHeader (s )
4642
4743 fmt .Fprintf (s , "Got:\t %+v\n " , f .got )
4844 fmt .Fprintf (s , "Wanted:\t %+v\n " , f .want )
4945
50- if f .cfg .context != "" {
51- fmt .Fprintf (s , "\n (%s)\n " , f .cfg .context )
52- }
46+ f .cfg .writeFooter (s )
47+
48+ return s .String ()
49+ }
5350
54- if f .cfg .reason != "" {
55- fmt .Fprintf (s , "\n Because: %s\n " , f .cfg .reason )
51+ // writeHeader writes the title block (leading blank line, title, underline, blank line)
52+ // to s. The underline is sized by rune count so multi-byte titles align correctly.
53+ func (c config ) writeHeader (s * strings.Builder ) {
54+ s .WriteByte ('\n' )
55+ s .WriteString (c .title )
56+ s .WriteByte ('\n' )
57+ s .WriteString (strings .Repeat ("-" , utf8 .RuneCountInString (c .title )))
58+ s .WriteString ("\n \n " )
59+ }
60+
61+ // writeFooter writes any optional context and reason lines to s.
62+ func (c config ) writeFooter (s * strings.Builder ) {
63+ if c .context != "" {
64+ fmt .Fprintf (s , "\n (%s)\n " , c .context )
5665 }
5766
58- return s .String ()
67+ if c .reason != "" {
68+ fmt .Fprintf (s , "\n Because: %s\n " , c .reason )
69+ }
5970}
6071
6172// Option is a configuration option for a test.
@@ -79,7 +90,7 @@ func (o option) apply(cfg *config) error {
7990// two floating point numbers before they are considered equal. This setting is only
8091// used in [NearlyEqual] and [NotNearlyEqual].
8192//
82- // Setting threshold to ±math.Inf is an error and will fail the test.
93+ // Setting threshold to ±[ math.Inf] is an error and will fail the test.
8394//
8495// The default is 1e-8, a sensible default for most cases.
8596func FloatEqualityThreshold (threshold float64 ) Option {
@@ -137,12 +148,12 @@ func Title(title string) Option {
137148// test.Ok(t, err, test.Context("something complicated failed"))
138149func Context (format string , args ... any ) Option {
139150 f := func (cfg * config ) error {
140- if format == "" {
151+ context := strings .TrimSpace (fmt .Sprintf (format , args ... ))
152+ if context == "" {
141153 return errors .New ("cannot set context to an empty string" )
142154 }
143155
144- context := fmt .Sprintf (format , args ... )
145- cfg .context = strings .TrimSpace (context )
156+ cfg .context = context
146157
147158 return nil
148159 }
0 commit comments