github.com/panekj/cli@v0.0.0-20230304125325-467dd2f3797e/cli/command/formatter/tabwriter/tabwriter_test.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package tabwriter
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"io"
    11  	"testing"
    12  )
    13  
    14  type buffer struct {
    15  	a []byte
    16  }
    17  
    18  func (b *buffer) init(n int) { b.a = make([]byte, 0, n) }
    19  
    20  func (b *buffer) clear() { b.a = b.a[0:0] }
    21  
    22  func (b *buffer) Write(buf []byte) (written int, err error) {
    23  	n := len(b.a)
    24  	m := len(buf)
    25  	if n+m <= cap(b.a) {
    26  		b.a = b.a[0 : n+m]
    27  		for i := 0; i < m; i++ {
    28  			b.a[n+i] = buf[i]
    29  		}
    30  	} else {
    31  		panic("buffer.Write: buffer too small")
    32  	}
    33  	return len(buf), nil
    34  }
    35  
    36  func (b *buffer) String() string { return string(b.a) }
    37  
    38  func write(t *testing.T, testname string, w *Writer, src string) {
    39  	written, err := io.WriteString(w, src)
    40  	if err != nil {
    41  		t.Errorf("--- test: %s\n--- src:\n%q\n--- write error: %v\n", testname, src, err)
    42  	}
    43  	if written != len(src) {
    44  		t.Errorf("--- test: %s\n--- src:\n%q\n--- written = %d, len(src) = %d\n", testname, src, written, len(src))
    45  	}
    46  }
    47  
    48  func verify(t *testing.T, testname string, w *Writer, b *buffer, src, expected string) {
    49  	err := w.Flush()
    50  	if err != nil {
    51  		t.Errorf("--- test: %s\n--- src:\n%q\n--- flush error: %v\n", testname, src, err)
    52  	}
    53  
    54  	res := b.String()
    55  	if res != expected {
    56  		t.Errorf("--- test: %s\n--- src:\n%q\n--- found:\n%q\n--- expected:\n%q\n", testname, src, res, expected)
    57  	}
    58  }
    59  
    60  func check(t *testing.T, testname string, minwidth, tabwidth, padding int, padchar byte, flags uint, src, expected string) {
    61  	var b buffer
    62  	b.init(1000)
    63  
    64  	var w Writer
    65  	w.Init(&b, minwidth, tabwidth, padding, padchar, flags)
    66  
    67  	// write all at once
    68  	title := testname + " (written all at once)"
    69  	b.clear()
    70  	write(t, title, &w, src)
    71  	verify(t, title, &w, &b, src, expected)
    72  
    73  	// write byte-by-byte
    74  	title = testname + " (written byte-by-byte)"
    75  	b.clear()
    76  	for i := 0; i < len(src); i++ {
    77  		write(t, title, &w, src[i:i+1])
    78  	}
    79  	verify(t, title, &w, &b, src, expected)
    80  
    81  	// write using Fibonacci slice sizes
    82  	title = testname + " (written in fibonacci slices)"
    83  	b.clear()
    84  	for i, d := 0, 0; i < len(src); {
    85  		write(t, title, &w, src[i:i+d])
    86  		i, d = i+d, d+1
    87  		if i+d > len(src) {
    88  			d = len(src) - i
    89  		}
    90  	}
    91  	verify(t, title, &w, &b, src, expected)
    92  }
    93  
    94  var tests = []struct {
    95  	testname                    string
    96  	minwidth, tabwidth, padding int
    97  	padchar                     byte
    98  	flags                       uint
    99  	src, expected               string
   100  }{
   101  	{
   102  		"1a",
   103  		8, 0, 1, '.', 0,
   104  		"",
   105  		"",
   106  	},
   107  
   108  	{
   109  		"1a debug",
   110  		8, 0, 1, '.', Debug,
   111  		"",
   112  		"",
   113  	},
   114  
   115  	{
   116  		"1b esc stripped",
   117  		8, 0, 1, '.', StripEscape,
   118  		"\xff\xff",
   119  		"",
   120  	},
   121  
   122  	{
   123  		"1b esc",
   124  		8, 0, 1, '.', 0,
   125  		"\xff\xff",
   126  		"\xff\xff",
   127  	},
   128  
   129  	{
   130  		"1c esc stripped",
   131  		8, 0, 1, '.', StripEscape,
   132  		"\xff\t\xff",
   133  		"\t",
   134  	},
   135  
   136  	{
   137  		"1c esc",
   138  		8, 0, 1, '.', 0,
   139  		"\xff\t\xff",
   140  		"\xff\t\xff",
   141  	},
   142  
   143  	{
   144  		"1d esc stripped",
   145  		8, 0, 1, '.', StripEscape,
   146  		"\xff\"foo\t\n\tbar\"\xff",
   147  		"\"foo\t\n\tbar\"",
   148  	},
   149  
   150  	{
   151  		"1d esc",
   152  		8, 0, 1, '.', 0,
   153  		"\xff\"foo\t\n\tbar\"\xff",
   154  		"\xff\"foo\t\n\tbar\"\xff",
   155  	},
   156  
   157  	{
   158  		"1e esc stripped",
   159  		8, 0, 1, '.', StripEscape,
   160  		"abc\xff\tdef", // unterminated escape
   161  		"abc\tdef",
   162  	},
   163  
   164  	{
   165  		"1e esc",
   166  		8, 0, 1, '.', 0,
   167  		"abc\xff\tdef", // unterminated escape
   168  		"abc\xff\tdef",
   169  	},
   170  
   171  	{
   172  		"2",
   173  		8, 0, 1, '.', 0,
   174  		"\n\n\n",
   175  		"\n\n\n",
   176  	},
   177  
   178  	{
   179  		"3",
   180  		8, 0, 1, '.', 0,
   181  		"a\nb\nc",
   182  		"a\nb\nc",
   183  	},
   184  
   185  	{
   186  		"4a",
   187  		8, 0, 1, '.', 0,
   188  		"\t", // '\t' terminates an empty cell on last line - nothing to print
   189  		"",
   190  	},
   191  
   192  	{
   193  		"4b",
   194  		8, 0, 1, '.', AlignRight,
   195  		"\t", // '\t' terminates an empty cell on last line - nothing to print
   196  		"",
   197  	},
   198  
   199  	{
   200  		"5",
   201  		8, 0, 1, '.', 0,
   202  		"*\t*",
   203  		"*.......*",
   204  	},
   205  
   206  	{
   207  		"5b",
   208  		8, 0, 1, '.', 0,
   209  		"*\t*\n",
   210  		"*.......*\n",
   211  	},
   212  
   213  	{
   214  		"5c",
   215  		8, 0, 1, '.', 0,
   216  		"*\t*\t",
   217  		"*.......*",
   218  	},
   219  
   220  	{
   221  		"5c debug",
   222  		8, 0, 1, '.', Debug,
   223  		"*\t*\t",
   224  		"*.......|*",
   225  	},
   226  
   227  	{
   228  		"5d",
   229  		8, 0, 1, '.', AlignRight,
   230  		"*\t*\t",
   231  		".......**",
   232  	},
   233  
   234  	{
   235  		"6",
   236  		8, 0, 1, '.', 0,
   237  		"\t\n",
   238  		"........\n",
   239  	},
   240  
   241  	{
   242  		"7a",
   243  		8, 0, 1, '.', 0,
   244  		"a) foo",
   245  		"a) foo",
   246  	},
   247  
   248  	{
   249  		"7b",
   250  		8, 0, 1, ' ', 0,
   251  		"b) foo\tbar",
   252  		"b) foo  bar",
   253  	},
   254  
   255  	{
   256  		"7c",
   257  		8, 0, 1, '.', 0,
   258  		"c) foo\tbar\t",
   259  		"c) foo..bar",
   260  	},
   261  
   262  	{
   263  		"7d",
   264  		8, 0, 1, '.', 0,
   265  		"d) foo\tbar\n",
   266  		"d) foo..bar\n",
   267  	},
   268  
   269  	{
   270  		"7e",
   271  		8, 0, 1, '.', 0,
   272  		"e) foo\tbar\t\n",
   273  		"e) foo..bar.....\n",
   274  	},
   275  
   276  	{
   277  		"7f",
   278  		8, 0, 1, '.', FilterHTML,
   279  		"f) f&lt;o\t<b>bar</b>\t\n",
   280  		"f) f&lt;o..<b>bar</b>.....\n",
   281  	},
   282  
   283  	{
   284  		"7g",
   285  		8, 0, 1, '.', FilterHTML,
   286  		"g) f&lt;o\t<b>bar</b>\t non-terminated entity &amp",
   287  		"g) f&lt;o..<b>bar</b>..... non-terminated entity &amp",
   288  	},
   289  
   290  	{
   291  		"7g debug",
   292  		8, 0, 1, '.', FilterHTML | Debug,
   293  		"g) f&lt;o\t<b>bar</b>\t non-terminated entity &amp",
   294  		"g) f&lt;o..|<b>bar</b>.....| non-terminated entity &amp",
   295  	},
   296  
   297  	{
   298  		"8",
   299  		8, 0, 1, '*', 0,
   300  		"Hello, world!\n",
   301  		"Hello, world!\n",
   302  	},
   303  
   304  	{
   305  		"9a",
   306  		1, 0, 0, '.', 0,
   307  		"1\t2\t3\t4\n" +
   308  			"11\t222\t3333\t44444\n",
   309  
   310  		"1.2..3...4\n" +
   311  			"11222333344444\n",
   312  	},
   313  
   314  	{
   315  		"9b",
   316  		1, 0, 0, '.', FilterHTML,
   317  		"1\t2<!---\f--->\t3\t4\n" + // \f inside HTML is ignored
   318  			"11\t222\t3333\t44444\n",
   319  
   320  		"1.2<!---\f--->..3...4\n" +
   321  			"11222333344444\n",
   322  	},
   323  
   324  	{
   325  		"9c",
   326  		1, 0, 0, '.', 0,
   327  		"1\t2\t3\t4\f" + // \f causes a newline and flush
   328  			"11\t222\t3333\t44444\n",
   329  
   330  		"1234\n" +
   331  			"11222333344444\n",
   332  	},
   333  
   334  	{
   335  		"9c debug",
   336  		1, 0, 0, '.', Debug,
   337  		"1\t2\t3\t4\f" + // \f causes a newline and flush
   338  			"11\t222\t3333\t44444\n",
   339  
   340  		"1|2|3|4\n" +
   341  			"---\n" +
   342  			"11|222|3333|44444\n",
   343  	},
   344  
   345  	{
   346  		"10a",
   347  		5, 0, 0, '.', 0,
   348  		"1\t2\t3\t4\n",
   349  		"1....2....3....4\n",
   350  	},
   351  
   352  	{
   353  		"10b",
   354  		5, 0, 0, '.', 0,
   355  		"1\t2\t3\t4\t\n",
   356  		"1....2....3....4....\n",
   357  	},
   358  
   359  	{
   360  		"11",
   361  		8, 0, 1, '.', 0,
   362  		"本\tb\tc\n" +
   363  			"aa\t\u672c\u672c\u672c\tcccc\tddddd\n" +
   364  			"aaa\tbbbb\n",
   365  
   366  		"本......b.......c\n" +
   367  			"aa......本本本..cccc....ddddd\n" +
   368  			"aaa.....bbbb\n",
   369  	},
   370  
   371  	{
   372  		"12a",
   373  		8, 0, 1, ' ', AlignRight,
   374  		"a\tè\tc\t\n" +
   375  			"aa\tèèè\tcccc\tddddd\t\n" +
   376  			"aaa\tèèèè\t\n",
   377  
   378  		"       a       è       c\n" +
   379  			"      aa     èèè    cccc   ddddd\n" +
   380  			"     aaa    èèèè\n",
   381  	},
   382  
   383  	{
   384  		"12b",
   385  		2, 0, 0, ' ', 0,
   386  		"a\tb\tc\n" +
   387  			"aa\tbbb\tcccc\n" +
   388  			"aaa\tbbbb\n",
   389  
   390  		"a  b  c\n" +
   391  			"aa bbbcccc\n" +
   392  			"aaabbbb\n",
   393  	},
   394  
   395  	{
   396  		"12c",
   397  		8, 0, 1, '_', 0,
   398  		"a\tb\tc\n" +
   399  			"aa\tbbb\tcccc\n" +
   400  			"aaa\tbbbb\n",
   401  
   402  		"a_______b_______c\n" +
   403  			"aa______bbb_____cccc\n" +
   404  			"aaa_____bbbb\n",
   405  	},
   406  
   407  	{
   408  		"13a",
   409  		4, 0, 1, '-', 0,
   410  		"4444\t日本語\t22\t1\t333\n" +
   411  			"999999999\t22\n" +
   412  			"7\t22\n" +
   413  			"\t\t\t88888888\n" +
   414  			"\n" +
   415  			"666666\t666666\t666666\t4444\n" +
   416  			"1\t1\t999999999\t0000000000\n",
   417  
   418  		"4444------日本語-22--1---333\n" +
   419  			"999999999-22\n" +
   420  			"7---------22\n" +
   421  			"------------------88888888\n" +
   422  			"\n" +
   423  			"666666-666666-666666----4444\n" +
   424  			"1------1------999999999-0000000000\n",
   425  	},
   426  
   427  	{
   428  		"13b",
   429  		4, 0, 3, '.', 0,
   430  		"4444\t333\t22\t1\t333\n" +
   431  			"999999999\t22\n" +
   432  			"7\t22\n" +
   433  			"\t\t\t88888888\n" +
   434  			"\n" +
   435  			"666666\t666666\t666666\t4444\n" +
   436  			"1\t1\t999999999\t0000000000\n",
   437  
   438  		"4444........333...22...1...333\n" +
   439  			"999999999...22\n" +
   440  			"7...........22\n" +
   441  			"....................88888888\n" +
   442  			"\n" +
   443  			"666666...666666...666666......4444\n" +
   444  			"1........1........999999999...0000000000\n",
   445  	},
   446  
   447  	{
   448  		"13c",
   449  		8, 8, 1, '\t', FilterHTML,
   450  		"4444\t333\t22\t1\t333\n" +
   451  			"999999999\t22\n" +
   452  			"7\t22\n" +
   453  			"\t\t\t88888888\n" +
   454  			"\n" +
   455  			"666666\t666666\t666666\t4444\n" +
   456  			"1\t1\t<font color=red attr=日本語>999999999</font>\t0000000000\n",
   457  
   458  		"4444\t\t333\t22\t1\t333\n" +
   459  			"999999999\t22\n" +
   460  			"7\t\t22\n" +
   461  			"\t\t\t\t88888888\n" +
   462  			"\n" +
   463  			"666666\t666666\t666666\t\t4444\n" +
   464  			"1\t1\t<font color=red attr=日本語>999999999</font>\t0000000000\n",
   465  	},
   466  
   467  	{
   468  		"14",
   469  		1, 0, 2, ' ', AlignRight,
   470  		".0\t.3\t2.4\t-5.1\t\n" +
   471  			"23.0\t12345678.9\t2.4\t-989.4\t\n" +
   472  			"5.1\t12.0\t2.4\t-7.0\t\n" +
   473  			".0\t0.0\t332.0\t8908.0\t\n" +
   474  			".0\t-.3\t456.4\t22.1\t\n" +
   475  			".0\t1.2\t44.4\t-13.3\t\t",
   476  
   477  		"    .0          .3    2.4    -5.1\n" +
   478  			"  23.0  12345678.9    2.4  -989.4\n" +
   479  			"   5.1        12.0    2.4    -7.0\n" +
   480  			"    .0         0.0  332.0  8908.0\n" +
   481  			"    .0         -.3  456.4    22.1\n" +
   482  			"    .0         1.2   44.4   -13.3",
   483  	},
   484  
   485  	{
   486  		"14 debug",
   487  		1, 0, 2, ' ', AlignRight | Debug,
   488  		".0\t.3\t2.4\t-5.1\t\n" +
   489  			"23.0\t12345678.9\t2.4\t-989.4\t\n" +
   490  			"5.1\t12.0\t2.4\t-7.0\t\n" +
   491  			".0\t0.0\t332.0\t8908.0\t\n" +
   492  			".0\t-.3\t456.4\t22.1\t\n" +
   493  			".0\t1.2\t44.4\t-13.3\t\t",
   494  
   495  		"    .0|          .3|    2.4|    -5.1|\n" +
   496  			"  23.0|  12345678.9|    2.4|  -989.4|\n" +
   497  			"   5.1|        12.0|    2.4|    -7.0|\n" +
   498  			"    .0|         0.0|  332.0|  8908.0|\n" +
   499  			"    .0|         -.3|  456.4|    22.1|\n" +
   500  			"    .0|         1.2|   44.4|   -13.3|",
   501  	},
   502  
   503  	{
   504  		"15a",
   505  		4, 0, 0, '.', 0,
   506  		"a\t\tb",
   507  		"a.......b",
   508  	},
   509  
   510  	{
   511  		"15b",
   512  		4, 0, 0, '.', DiscardEmptyColumns,
   513  		"a\t\tb", // htabs - do not discard column
   514  		"a.......b",
   515  	},
   516  
   517  	{
   518  		"15c",
   519  		4, 0, 0, '.', DiscardEmptyColumns,
   520  		"a\v\vb",
   521  		"a...b",
   522  	},
   523  
   524  	{
   525  		"15d",
   526  		4, 0, 0, '.', AlignRight | DiscardEmptyColumns,
   527  		"a\v\vb",
   528  		"...ab",
   529  	},
   530  
   531  	{
   532  		"16a",
   533  		100, 100, 0, '\t', 0,
   534  		"a\tb\t\td\n" +
   535  			"a\tb\t\td\te\n" +
   536  			"a\n" +
   537  			"a\tb\tc\td\n" +
   538  			"a\tb\tc\td\te\n",
   539  
   540  		"a\tb\t\td\n" +
   541  			"a\tb\t\td\te\n" +
   542  			"a\n" +
   543  			"a\tb\tc\td\n" +
   544  			"a\tb\tc\td\te\n",
   545  	},
   546  
   547  	{
   548  		"16b",
   549  		100, 100, 0, '\t', DiscardEmptyColumns,
   550  		"a\vb\v\vd\n" +
   551  			"a\vb\v\vd\ve\n" +
   552  			"a\n" +
   553  			"a\vb\vc\vd\n" +
   554  			"a\vb\vc\vd\ve\n",
   555  
   556  		"a\tb\td\n" +
   557  			"a\tb\td\te\n" +
   558  			"a\n" +
   559  			"a\tb\tc\td\n" +
   560  			"a\tb\tc\td\te\n",
   561  	},
   562  
   563  	{
   564  		"16b debug",
   565  		100, 100, 0, '\t', DiscardEmptyColumns | Debug,
   566  		"a\vb\v\vd\n" +
   567  			"a\vb\v\vd\ve\n" +
   568  			"a\n" +
   569  			"a\vb\vc\vd\n" +
   570  			"a\vb\vc\vd\ve\n",
   571  
   572  		"a\t|b\t||d\n" +
   573  			"a\t|b\t||d\t|e\n" +
   574  			"a\n" +
   575  			"a\t|b\t|c\t|d\n" +
   576  			"a\t|b\t|c\t|d\t|e\n",
   577  	},
   578  
   579  	{
   580  		"16c",
   581  		100, 100, 0, '\t', DiscardEmptyColumns,
   582  		"a\tb\t\td\n" + // hard tabs - do not discard column
   583  			"a\tb\t\td\te\n" +
   584  			"a\n" +
   585  			"a\tb\tc\td\n" +
   586  			"a\tb\tc\td\te\n",
   587  
   588  		"a\tb\t\td\n" +
   589  			"a\tb\t\td\te\n" +
   590  			"a\n" +
   591  			"a\tb\tc\td\n" +
   592  			"a\tb\tc\td\te\n",
   593  	},
   594  
   595  	{
   596  		"16c debug",
   597  		100, 100, 0, '\t', DiscardEmptyColumns | Debug,
   598  		"a\tb\t\td\n" + // hard tabs - do not discard column
   599  			"a\tb\t\td\te\n" +
   600  			"a\n" +
   601  			"a\tb\tc\td\n" +
   602  			"a\tb\tc\td\te\n",
   603  
   604  		"a\t|b\t|\t|d\n" +
   605  			"a\t|b\t|\t|d\t|e\n" +
   606  			"a\n" +
   607  			"a\t|b\t|c\t|d\n" +
   608  			"a\t|b\t|c\t|d\t|e\n",
   609  	},
   610  }
   611  
   612  func Test(t *testing.T) {
   613  	for _, e := range tests {
   614  		check(t, e.testname, e.minwidth, e.tabwidth, e.padding, e.padchar, e.flags, e.src, e.expected)
   615  	}
   616  }
   617  
   618  type panicWriter struct{}
   619  
   620  func (panicWriter) Write([]byte) (int, error) {
   621  	panic("cannot write")
   622  }
   623  
   624  func wantPanicString(t *testing.T, want string) {
   625  	if e := recover(); e != nil {
   626  		got, ok := e.(string)
   627  		switch {
   628  		case !ok:
   629  			t.Errorf("got %v (%T), want panic string", e, e)
   630  		case got != want:
   631  			t.Errorf("wrong panic message: got %q, want %q", got, want)
   632  		}
   633  	}
   634  }
   635  
   636  func TestPanicDuringFlush(t *testing.T) {
   637  	defer wantPanicString(t, "tabwriter: panic during Flush")
   638  	var p panicWriter
   639  	w := new(Writer)
   640  	w.Init(p, 0, 0, 5, ' ', 0)
   641  	io.WriteString(w, "a")
   642  	w.Flush()
   643  	t.Errorf("failed to panic during Flush")
   644  }
   645  
   646  func TestPanicDuringWrite(t *testing.T) {
   647  	defer wantPanicString(t, "tabwriter: panic during Write")
   648  	var p panicWriter
   649  	w := new(Writer)
   650  	w.Init(p, 0, 0, 5, ' ', 0)
   651  	io.WriteString(w, "a\n\n") // the second \n triggers a call to w.Write and thus a panic
   652  	t.Errorf("failed to panic during Write")
   653  }
   654  
   655  func BenchmarkTable(b *testing.B) {
   656  	for _, w := range [...]int{1, 10, 100} {
   657  		// Build a line with w cells.
   658  		line := bytes.Repeat([]byte("a\t"), w)
   659  		line = append(line, '\n')
   660  		for _, h := range [...]int{10, 1000, 100000} {
   661  			b.Run(fmt.Sprintf("%dx%d", w, h), func(b *testing.B) {
   662  				b.Run("new", func(b *testing.B) {
   663  					b.ReportAllocs()
   664  					for i := 0; i < b.N; i++ {
   665  						w := NewWriter(io.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings
   666  						// Write the line h times.
   667  						for j := 0; j < h; j++ {
   668  							w.Write(line)
   669  						}
   670  						w.Flush()
   671  					}
   672  				})
   673  
   674  				b.Run("reuse", func(b *testing.B) {
   675  					b.ReportAllocs()
   676  					w := NewWriter(io.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings
   677  					for i := 0; i < b.N; i++ {
   678  						// Write the line h times.
   679  						for j := 0; j < h; j++ {
   680  							w.Write(line)
   681  						}
   682  						w.Flush()
   683  					}
   684  				})
   685  			})
   686  		}
   687  	}
   688  }
   689  
   690  func BenchmarkPyramid(b *testing.B) {
   691  	for _, x := range [...]int{10, 100, 1000} {
   692  		// Build a line with x cells.
   693  		line := bytes.Repeat([]byte("a\t"), x)
   694  		b.Run(fmt.Sprintf("%d", x), func(b *testing.B) {
   695  			b.ReportAllocs()
   696  			for i := 0; i < b.N; i++ {
   697  				w := NewWriter(io.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings
   698  				// Write increasing prefixes of that line.
   699  				for j := 0; j < x; j++ {
   700  					w.Write(line[:j*2])
   701  					w.Write([]byte{'\n'})
   702  				}
   703  				w.Flush()
   704  			}
   705  		})
   706  	}
   707  }
   708  
   709  func BenchmarkRagged(b *testing.B) {
   710  	var lines [8][]byte
   711  	for i, w := range [8]int{6, 2, 9, 5, 5, 7, 3, 8} {
   712  		// Build a line with w cells.
   713  		lines[i] = bytes.Repeat([]byte("a\t"), w)
   714  	}
   715  	for _, h := range [...]int{10, 100, 1000} {
   716  		b.Run(fmt.Sprintf("%d", h), func(b *testing.B) {
   717  			b.ReportAllocs()
   718  			for i := 0; i < b.N; i++ {
   719  				w := NewWriter(io.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings
   720  				// Write the lines in turn h times.
   721  				for j := 0; j < h; j++ {
   722  					w.Write(lines[j%len(lines)])
   723  					w.Write([]byte{'\n'})
   724  				}
   725  				w.Flush()
   726  			}
   727  		})
   728  	}
   729  }
   730  
   731  const codeSnippet = `
   732  some command
   733  
   734  foo	# aligned
   735  barbaz	# comments
   736  
   737  but
   738  mostly
   739  single
   740  cell
   741  lines
   742  `
   743  
   744  func BenchmarkCode(b *testing.B) {
   745  	b.ReportAllocs()
   746  	for i := 0; i < b.N; i++ {
   747  		w := NewWriter(io.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings
   748  		// The code is small, so it's reasonable for the tabwriter user
   749  		// to write it all at once, or buffer the writes.
   750  		w.Write([]byte(codeSnippet))
   751  		w.Flush()
   752  	}
   753  }