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