codeberg.org/gruf/go-format@v1.0.6/print_test.go (about)

     1  package format_test
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"net/http"
     8  	"net/url"
     9  	"reflect"
    10  	"strconv"
    11  	"sync"
    12  	"testing"
    13  	"time"
    14  
    15  	"codeberg.org/gruf/go-format"
    16  )
    17  
    18  // A is a test structure.
    19  type A struct {
    20  	A string
    21  	B *string
    22  }
    23  
    24  // B is a test structure with unexported fields.
    25  type B struct {
    26  	a string
    27  	b *string
    28  }
    29  
    30  // C is a test structure with fmt.Stringer implementation.
    31  type C struct{}
    32  
    33  func (c C) String() string {
    34  	return "c.String()"
    35  }
    36  
    37  // D is a test structure with format.Formattable implementation.
    38  type D struct{}
    39  
    40  func (d D) AppendFormat(b []byte) []byte {
    41  	return append(b, `d.AppendFormat()`...)
    42  }
    43  
    44  // PanicTest is a test structure with a fmt.Stringer implementation that panics.
    45  type PanicTest int
    46  
    47  func (t PanicTest) String() string {
    48  	panic(`oh no! a panic`)
    49  }
    50  
    51  // appendTests are just a list of arguments to
    52  // format and append to buffer, these test results
    53  // are not checked these are to ensure safety
    54  var appendTests = []interface{}{}
    55  
    56  // printfTests provide a list of format strings, with
    57  // single argument and expected results. Intended to
    58  // test that operands produce expected results.
    59  var printfTests = []struct {
    60  	Fmt string
    61  	Arg interface{}
    62  	Out string
    63  }{
    64  	// default format
    65  	{
    66  		Fmt: `{}`,
    67  		Arg: nil,
    68  		Out: `nil`,
    69  	},
    70  	{
    71  		Fmt: `{}`,
    72  		Arg: (*int)(nil),
    73  		Out: `nil`,
    74  	},
    75  	{
    76  		Fmt: `{}`,
    77  		Arg: time.Second,
    78  		Out: time.Second.String(),
    79  	},
    80  	{
    81  		Fmt: `{}`,
    82  		Arg: `hello`,
    83  		Out: `hello`,
    84  	},
    85  	{
    86  		Fmt: `{}`,
    87  		Arg: `hello world`,
    88  		Out: `hello world`,
    89  	},
    90  	{
    91  		Fmt: `{}`,
    92  		Arg: "hello\nworld\n",
    93  		Out: "hello\nworld\n",
    94  	},
    95  	{
    96  		Fmt: `{}`,
    97  		Arg: errors.New("error!"),
    98  		Out: `error!`,
    99  	},
   100  	{
   101  		Fmt: `{}`,
   102  		Arg: A{},
   103  		Out: `{A="" B=nil}`,
   104  	},
   105  	{
   106  		Fmt: `{}`,
   107  		Arg: B{},
   108  		Out: `{a="" b=nil}`,
   109  	},
   110  	{
   111  		Fmt: `{}`,
   112  		Arg: C{},
   113  		Out: `c.String()`,
   114  	},
   115  	{
   116  		Fmt: `{}`,
   117  		Arg: D{},
   118  		Out: `d.AppendFormat()`,
   119  	},
   120  	{
   121  		Fmt: `{}`,
   122  		Arg: PanicTest(0),
   123  		Out: `!{PANIC="oh no! a panic"}`,
   124  	},
   125  	{
   126  		Fmt: `{}`,
   127  		Arg: int8(0),
   128  		Out: `0`,
   129  	},
   130  	{
   131  		Fmt: `{}`,
   132  		Arg: int16(0),
   133  		Out: `0`,
   134  	},
   135  	{
   136  		Fmt: `{}`,
   137  		Arg: int32(0),
   138  		Out: `0`,
   139  	},
   140  	{
   141  		Fmt: `{}`,
   142  		Arg: int64(0),
   143  		Out: `0`,
   144  	},
   145  	{
   146  		Fmt: `{}`,
   147  		Arg: uint8(0), // uint8=byte
   148  		Out: "\x00",   // formatted as byte
   149  	},
   150  	{
   151  		Fmt: `{}`,
   152  		Arg: uint16(0),
   153  		Out: `0`,
   154  	},
   155  	{
   156  		Fmt: `{}`,
   157  		Arg: uint32(0),
   158  		Out: `0`,
   159  	},
   160  	{
   161  		Fmt: `{}`,
   162  		Arg: uint64(0),
   163  		Out: `0`,
   164  	},
   165  	{
   166  		Fmt: `{}`,
   167  		Arg: float32(0.0),
   168  		Out: `0`,
   169  	},
   170  	{
   171  		Fmt: `{}`,
   172  		Arg: float64(0.0),
   173  		Out: `0`,
   174  	},
   175  	{
   176  		Fmt: `{}`,
   177  		Arg: complex64(0),
   178  		Out: `0+0i`,
   179  	},
   180  	{
   181  		Fmt: `{}`,
   182  		Arg: complex128(0),
   183  		Out: `0+0i`,
   184  	},
   185  	{
   186  		Fmt: `{}`,
   187  		Arg: []byte(`hello world`),
   188  		Out: `[h,e,l,l,o, ,w,o,r,l,d]`,
   189  	},
   190  	{
   191  		Fmt: `{}`,
   192  		Arg: byte('?'),
   193  		Out: `?`,
   194  	},
   195  	{
   196  		Fmt: `{}`,
   197  		Arg: byte('\n'),
   198  		Out: "\n",
   199  	},
   200  
   201  	// key format
   202  	{
   203  		Fmt: `{:k}`,
   204  		Arg: nil,
   205  		Out: `nil`,
   206  	},
   207  	{
   208  		Fmt: `{:k}`,
   209  		Arg: (*int)(nil),
   210  		Out: `nil`,
   211  	},
   212  	{
   213  		Fmt: `{:k}`,
   214  		Arg: time.Second,
   215  		Out: time.Second.String(),
   216  	},
   217  	{
   218  		Fmt: `{:k}`,
   219  		Arg: `hello`,
   220  		Out: `hello`,
   221  	},
   222  	{
   223  		Fmt: `{:k}`,
   224  		Arg: `hello world`,
   225  		Out: `"hello world"`,
   226  	},
   227  	{
   228  		Fmt: `{:k}`,
   229  		Arg: "hello\nworld\n",
   230  		Out: `"hello\nworld\n"`,
   231  	},
   232  	{
   233  		Fmt: `{:k}`,
   234  		Arg: errors.New("error!"),
   235  		Out: `error!`,
   236  	},
   237  	{
   238  		Fmt: `{:k}`,
   239  		Arg: A{},
   240  		Out: `{A="" B=nil}`,
   241  	},
   242  	{
   243  		Fmt: `{:k}`,
   244  		Arg: B{},
   245  		Out: `{a="" b=nil}`,
   246  	},
   247  	{
   248  		Fmt: `{:k}`,
   249  		Arg: C{},
   250  		Out: `c.String()`,
   251  	},
   252  	{
   253  		Fmt: `{:k}`,
   254  		Arg: D{},
   255  		Out: `d.AppendFormat()`,
   256  	},
   257  	{
   258  		Fmt: `{:k}`,
   259  		Arg: PanicTest(0),
   260  		Out: `!{PANIC="oh no! a panic"}`,
   261  	},
   262  	{
   263  		Fmt: `{:k}`,
   264  		Arg: []byte(`hello world`),
   265  		Out: `[h,e,l,l,o, ,w,o,r,l,d]`,
   266  	},
   267  	{
   268  		Fmt: `{:k}`,
   269  		Arg: byte('?'),
   270  		Out: `'?'`,
   271  	},
   272  	{
   273  		Fmt: `{:k}`,
   274  		Arg: byte('\n'),
   275  		Out: `'\n'`,
   276  	},
   277  
   278  	// value format
   279  	{
   280  		Fmt: `{:v}`,
   281  		Arg: nil,
   282  		Out: `nil`,
   283  	},
   284  	{
   285  		Fmt: `{:v}`,
   286  		Arg: (*int)(nil),
   287  		Out: `nil`,
   288  	},
   289  	{
   290  		Fmt: `{:v}`,
   291  		Arg: time.Second,
   292  		Out: `"` + time.Second.String() + `"`,
   293  	},
   294  	{
   295  		Fmt: `{:v}`,
   296  		Arg: `hello`,
   297  		Out: `"hello"`,
   298  	},
   299  	{
   300  		Fmt: `{:v}`,
   301  		Arg: `hello world`,
   302  		Out: `"hello world"`,
   303  	},
   304  	{
   305  		Fmt: `{:v}`,
   306  		Arg: "hello\nworld\n",
   307  		Out: `"hello\nworld\n"`,
   308  	},
   309  	{
   310  		Fmt: `{:v}`,
   311  		Arg: errors.New("error!"),
   312  		Out: `"error!"`,
   313  	},
   314  	{
   315  		Fmt: `{:v}`,
   316  		Arg: A{},
   317  		Out: `{A="" B=nil}`,
   318  	},
   319  	{
   320  		Fmt: `{:v}`,
   321  		Arg: B{},
   322  		Out: `{a="" b=nil}`,
   323  	},
   324  	{
   325  		Fmt: `{:v}`,
   326  		Arg: C{},
   327  		Out: `"c.String()"`,
   328  	},
   329  	{
   330  		Fmt: `{:v}`,
   331  		Arg: D{},
   332  		Out: `d.AppendFormat()`,
   333  	},
   334  	{
   335  		Fmt: `{:v}`,
   336  		Arg: PanicTest(0),
   337  		Out: `!{PANIC="oh no! a panic"}`,
   338  	},
   339  	{
   340  		Fmt: `{:v}`,
   341  		Arg: []byte(`hello world`),
   342  		Out: `[h,e,l,l,o, ,w,o,r,l,d]`,
   343  	},
   344  	{
   345  		Fmt: `{:v}`,
   346  		Arg: byte('?'),
   347  		Out: `'?'`,
   348  	},
   349  	{
   350  		Fmt: `{:v}`,
   351  		Arg: byte('\n'),
   352  		Out: `'\n'`,
   353  	},
   354  
   355  	// verbose format
   356  	{
   357  		Fmt: `{:?}`,
   358  		Arg: nil,
   359  		Out: `nil`,
   360  	},
   361  	{
   362  		Fmt: `{:?}`,
   363  		Arg: (*int)(nil),
   364  		Out: `(*int)(nil)`,
   365  	},
   366  	{
   367  		Fmt: `{:?}`,
   368  		Arg: time.Second,
   369  		Out: strconv.FormatInt(int64(time.Second), 10),
   370  	},
   371  	{
   372  		Fmt: `{:?}`,
   373  		Arg: `hello`,
   374  		Out: `"hello"`,
   375  	},
   376  	{
   377  		Fmt: `{:?}`,
   378  		Arg: `hello world`,
   379  		Out: `"hello world"`,
   380  	},
   381  	{
   382  		Fmt: `{:?}`,
   383  		Arg: "hello\nworld\n",
   384  		Out: "\"hello\nworld\n\"",
   385  	},
   386  	{
   387  		Fmt: `{:?}`,
   388  		Arg: errors.New("error!"), //nolint
   389  		Out: `*errors.errorString{s="error!"}`,
   390  	},
   391  	{
   392  		Fmt: `{:?}`,
   393  		Arg: A{},
   394  		Out: `format_test.A{A="" B=(*string)(nil)}`,
   395  	},
   396  	{
   397  		Fmt: `{:?}`,
   398  		Arg: B{},
   399  		Out: `format_test.B{a="" b=(*string)(nil)}`,
   400  	},
   401  	{
   402  		Fmt: `{:?}`,
   403  		Arg: C{},
   404  		Out: `format_test.C{}`,
   405  	},
   406  	{
   407  		Fmt: `{:?}`,
   408  		Arg: D{},
   409  		Out: `format_test.D{}`,
   410  	},
   411  	{
   412  		Fmt: `{:?}`,
   413  		Arg: PanicTest(0),
   414  		Out: `0`,
   415  	},
   416  	{
   417  		Fmt: `{:?}`,
   418  		Arg: []byte(`hello world`),
   419  		Out: `[h,e,l,l,o, ,w,o,r,l,d]`,
   420  	},
   421  	{
   422  		Fmt: `{:?}`,
   423  		Arg: byte('?'),
   424  		Out: `'?'`,
   425  	},
   426  	{
   427  		Fmt: `{:?}`,
   428  		Arg: byte('\n'),
   429  		Out: `'\n'`,
   430  	},
   431  
   432  	// type format
   433  	{
   434  		Fmt: `{:T}`,
   435  		Arg: 0,
   436  		Out: reflect.TypeOf(0).String(),
   437  	},
   438  	{
   439  		Fmt: `{:T}`,
   440  		Arg: uint64(0),
   441  		Out: reflect.TypeOf(uint64(0)).String(),
   442  	},
   443  	{
   444  		Fmt: `{:T}`,
   445  		Arg: float64(0),
   446  		Out: reflect.TypeOf(float64(0)).String(),
   447  	},
   448  	{
   449  		Fmt: `{:T}`,
   450  		Arg: time.Time{},
   451  		Out: reflect.TypeOf(time.Time{}).String(),
   452  	},
   453  	{
   454  		Fmt: `{:T}`,
   455  		Arg: time.Duration(0),
   456  		Out: reflect.TypeOf(time.Duration(0)).String(),
   457  	},
   458  	{
   459  		Fmt: `{:T}`,
   460  		Arg: &time.Time{},
   461  		Out: reflect.TypeOf(&time.Time{}).String(),
   462  	},
   463  	{
   464  		Fmt: `{:T}`,
   465  		Arg: nil,
   466  		Out: `nil`,
   467  	},
   468  }
   469  
   470  // printfMultiTests provide a list of more complex format
   471  // strings, with any number of arguments and expected results.
   472  // Intended to test that string format parsing and formatting
   473  // produces expected results.
   474  var printfMultiTests = []struct {
   475  	Fmt string
   476  	Arg []interface{}
   477  	Out string
   478  }{}
   479  
   480  var fmtTests = []interface{}{
   481  	"hello world",
   482  	http.Server{},
   483  	sync.Mutex{},
   484  	sync.WaitGroup{},
   485  	(*url.URL)(nil),
   486  	http.Request{},
   487  	0,
   488  	1,
   489  	2,
   490  	3,
   491  	4,
   492  	5,
   493  	time.Second * 30,
   494  	map[string]string{
   495  		"hello": "world",
   496  		"key":   "value",
   497  	},
   498  	[]int{10, 11, 12, 13, 14, 15},
   499  	[]interface{}{"hello", "world", 12.2, 12, []byte("bytes")},
   500  	[]string{"hello", "world", "string", "array"},
   501  	',',
   502  	420.69,
   503  	time.Time{},
   504  	A{},
   505  	B{},
   506  	C{},
   507  	D{},
   508  	PanicTest(0),
   509  }
   510  
   511  func TestAppend(t *testing.T) {
   512  	buf := format.Buffer{}
   513  	for _, arg := range appendTests {
   514  		format.Append(&buf, arg)
   515  		buf.Reset()
   516  	}
   517  }
   518  
   519  func TestPrintf(t *testing.T) {
   520  	for _, test := range printfTests {
   521  		out := format.Sprintf(test.Fmt, test.Arg)
   522  		if out != test.Out {
   523  			t.Fatalf("printf did not produce expected results\n"+
   524  				"input={%q, %#v}\n"+
   525  				"expect=%s\n"+
   526  				"actual=%s\n",
   527  				test.Fmt,
   528  				test.Arg,
   529  				test.Out,
   530  				out,
   531  			)
   532  		}
   533  	}
   534  }
   535  
   536  func TestPrintfMulti(t *testing.T) {
   537  	for _, test := range printfMultiTests {
   538  		out := format.Sprintf(test.Fmt, test.Arg...)
   539  		if out != test.Out {
   540  			t.Fatalf("printf (multi arg) did not produce expected results\n"+
   541  				"input={%q, %#v}\n"+
   542  				"expect=%s\n"+
   543  				"actual=%s\n",
   544  				test.Fmt,
   545  				test.Arg,
   546  				test.Out,
   547  				out,
   548  			)
   549  		}
   550  	}
   551  }
   552  
   553  func BenchmarkFprintf(b *testing.B) {
   554  	b.ReportAllocs()
   555  	b.ResetTimer()
   556  	b.RunParallel(func(pb *testing.PB) {
   557  		for pb.Next() {
   558  			for i := range fmtTests {
   559  				// Perform both non-verbose and verbose
   560  				format.Fprintf(io.Discard, "{}", fmtTests[i])
   561  				format.Fprintf(io.Discard, "{:?}", fmtTests[i])
   562  			}
   563  		}
   564  	})
   565  }
   566  
   567  func BenchmarkFmtFprintf(b *testing.B) {
   568  	b.ReportAllocs()
   569  	b.ResetTimer()
   570  	b.RunParallel(func(pb *testing.PB) {
   571  		for pb.Next() {
   572  			for i := range fmtTests {
   573  				// Perform both non-verbose and verbose
   574  				fmt.Fprintf(io.Discard, "%v", fmtTests[i])
   575  				fmt.Fprintf(io.Discard, "%#v", fmtTests[i])
   576  			}
   577  		}
   578  	})
   579  }