Ivan Pilat | 4ae5581 | 2015-07-14 22:02:59 -0700 | [diff] [blame] | 1 | // Copyright 2015 The Vanadium 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 writer_test |
| 6 | |
| 7 | import ( |
| 8 | "bytes" |
| 9 | "encoding/json" |
| 10 | "testing" |
| 11 | "time" |
| 12 | |
| 13 | "v.io/syncbase/v23/syncbase/nosql" |
| 14 | db "v.io/syncbase/x/ref/syncbase/sb51/internal/demodb" |
| 15 | "v.io/syncbase/x/ref/syncbase/sb51/internal/writer" |
| 16 | "v.io/v23/vdl" |
| 17 | ) |
| 18 | |
| 19 | type fakeResultStream struct { |
| 20 | rows [][]*vdl.Value |
| 21 | curr int |
| 22 | } |
| 23 | |
| 24 | func newResultStream(iRows [][]interface{}) nosql.ResultStream { |
| 25 | vRows := make([][]*vdl.Value, len(iRows)) |
| 26 | for i, iRow := range iRows { |
| 27 | vRow := make([]*vdl.Value, len(iRow)) |
| 28 | for j, iCol := range iRow { |
| 29 | vRow[j] = vdl.ValueOf(iCol) |
| 30 | } |
| 31 | vRows[i] = vRow |
| 32 | } |
| 33 | return &fakeResultStream{ |
| 34 | rows: vRows, |
| 35 | curr: -1, |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | func (f *fakeResultStream) Advance() bool { |
| 40 | f.curr++ |
| 41 | return f.curr < len(f.rows) |
| 42 | } |
| 43 | |
| 44 | func (f *fakeResultStream) Result() []*vdl.Value { |
| 45 | if f.curr == -1 { |
| 46 | panic("call advance first") |
| 47 | } |
| 48 | return f.rows[f.curr] |
| 49 | } |
| 50 | |
| 51 | func (f *fakeResultStream) Err() error { |
| 52 | return nil |
| 53 | } |
| 54 | |
| 55 | func (f *fakeResultStream) Cancel() { |
| 56 | // Nothing to do. |
| 57 | } |
| 58 | |
| 59 | func TestWriteTable(t *testing.T) { |
| 60 | type testCase struct { |
| 61 | columns []string |
| 62 | rows [][]interface{} |
| 63 | // To make the test cases easier to read, output should have a leading |
| 64 | // newline. |
| 65 | output string |
| 66 | } |
| 67 | tests := []testCase{ |
| 68 | { |
| 69 | []string{"c1", "c2"}, |
| 70 | [][]interface{}{ |
| 71 | {5, "foo"}, |
| 72 | {6, "bar"}, |
| 73 | }, |
| 74 | ` |
| 75 | +----+-----+ |
| 76 | | c1 | c2 | |
| 77 | +----+-----+ |
| 78 | | 5 | foo | |
| 79 | | 6 | bar | |
| 80 | +----+-----+ |
| 81 | `, |
| 82 | }, |
| 83 | { |
| 84 | []string{"c1", "c2"}, |
| 85 | [][]interface{}{ |
| 86 | {500, "foo"}, |
| 87 | {6, "barbaz"}, |
| 88 | }, |
| 89 | ` |
| 90 | +-----+--------+ |
| 91 | | c1 | c2 | |
| 92 | +-----+--------+ |
| 93 | | 500 | foo | |
| 94 | | 6 | barbaz | |
| 95 | +-----+--------+ |
| 96 | `, |
| 97 | }, |
| 98 | { |
| 99 | []string{"c1", "reallylongcolumnheader"}, |
| 100 | [][]interface{}{ |
| 101 | {5, "foo"}, |
| 102 | {6, "bar"}, |
| 103 | }, |
| 104 | ` |
| 105 | +----+------------------------+ |
| 106 | | c1 | reallylongcolumnheader | |
| 107 | +----+------------------------+ |
| 108 | | 5 | foo | |
| 109 | | 6 | bar | |
| 110 | +----+------------------------+ |
| 111 | `, |
| 112 | }, |
| 113 | { // Numbers. |
| 114 | []string{"byte", "uint16", "uint32", "uint64", "int16", "int32", "int64", |
| 115 | "float32", "float64", "complex64", "complex128"}, |
| 116 | [][]interface{}{ |
| 117 | { |
| 118 | byte(12), uint16(1234), uint32(5678), uint64(999888777666), int16(9876), int32(876543), int64(128), |
| 119 | float32(3.14159), float64(2.71828182846), complex64(123.0 + 7.0i), complex128(456.789 + 10.1112i), |
| 120 | }, |
| 121 | { |
| 122 | byte(9), uint16(99), uint32(999), uint64(9999999), int16(9), int32(99), int64(88), |
| 123 | float32(1.41421356237), float64(1.73205080757), complex64(9.87 + 7.65i), complex128(4.32 + 1.0i), |
| 124 | }, |
| 125 | }, |
| 126 | ` |
| 127 | +------+--------+--------+--------------+-------+--------+-------+--------------------+---------------+--------------------------------------+------------------+ |
| 128 | | byte | uint16 | uint32 | uint64 | int16 | int32 | int64 | float32 | float64 | complex64 | complex128 | |
| 129 | +------+--------+--------+--------------+-------+--------+-------+--------------------+---------------+--------------------------------------+------------------+ |
| 130 | | 12 | 1234 | 5678 | 999888777666 | 9876 | 876543 | 128 | 3.141590118408203 | 2.71828182846 | 123+7i | 456.789+10.1112i | |
| 131 | | 9 | 99 | 999 | 9999999 | 9 | 99 | 88 | 1.4142135381698608 | 1.73205080757 | 9.869999885559082+7.650000095367432i | 4.32+1i | |
| 132 | +------+--------+--------+--------------+-------+--------+-------+--------------------+---------------+--------------------------------------+------------------+ |
| 133 | `, |
| 134 | }, |
| 135 | { // Strings with whitespace should be printed literally. |
| 136 | []string{"c1", "c2"}, |
| 137 | [][]interface{}{ |
| 138 | {"foo\tbar", "foo\nbar"}, |
| 139 | }, |
| 140 | ` |
| 141 | +---------+---------+ |
| 142 | | c1 | c2 | |
| 143 | +---------+---------+ |
| 144 | | foo bar | foo |
| 145 | bar | |
| 146 | +---------+---------+ |
| 147 | `, |
| 148 | }, |
| 149 | { // nil is shown as blank. |
| 150 | []string{"c1"}, |
| 151 | [][]interface{}{ |
| 152 | {nil}, |
| 153 | }, |
| 154 | ` |
| 155 | +----+ |
| 156 | | c1 | |
| 157 | +----+ |
| 158 | | | |
| 159 | +----+ |
| 160 | `, |
| 161 | }, |
| 162 | { |
| 163 | []string{"c1"}, |
| 164 | [][]interface{}{ |
| 165 | {db.Customer{"John Smith", 1, true, db.AddressInfo{"1 Main St.", "Palo Alto", "CA", "94303"}, db.CreditReport{Agency: db.CreditAgencyEquifax, Report: db.AgencyReportEquifaxReport{db.EquifaxCreditReport{'A'}}}}}, |
| 166 | {db.Invoice{1, 1000, 42, db.AddressInfo{"1 Main St.", "Palo Alto", "CA", "94303"}}}, |
| 167 | }, |
| 168 | ` |
| 169 | +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ |
| 170 | | c1 | |
| 171 | +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ |
| 172 | | {Name: "John Smith", Id: 1, Active: true, Address: {Street: "1 Main St.", City: "Palo Alto", State: "CA", Zip: "94303"}, Credit: {Agency: Equifax, Report: EquifaxReport: {Rating: 65}}} | |
| 173 | | {CustId: 1, InvoiceNum: 1000, Amount: 42, ShipTo: {Street: "1 Main St.", City: "Palo Alto", State: "CA", Zip: "94303"}} | |
| 174 | +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ |
| 175 | `, |
| 176 | }, |
| 177 | { |
| 178 | []string{"c1"}, |
| 179 | [][]interface{}{ |
| 180 | {db.Composite{db.Array2String{"foo", "棎鶊鵱"}, []int32{1, 2}, map[int32]struct{}{1: struct{}{}, 2: struct{}{}}, map[string]int32{"foo": 1, "bar": 2}}}, |
| 181 | }, |
| 182 | ` |
| 183 | +----------------------------------------------------------------------------------+ |
| 184 | | c1 | |
| 185 | +----------------------------------------------------------------------------------+ |
| 186 | | {Arr: ["foo", "棎鶊鵱"], ListInt: [1, 2], MySet: {1, 2}, Map: {"bar": 2, "foo": 1}} | |
| 187 | +----------------------------------------------------------------------------------+ |
| 188 | `, |
| 189 | }, |
| 190 | { // Types not built in to Go. |
| 191 | []string{"time", "type", "union", "enum", "set"}, |
| 192 | [][]interface{}{ |
| 193 | {time.Unix(13377331, 0), vdl.TypeOf(map[float32]struct{ B bool }{}), db.TitleOrValueTypeTitle{"dahar master"}, db.ExperianRatingBad, map[int32]struct{}{47: struct{}{}}}, |
| 194 | }, |
| 195 | ` |
| 196 | +-------------------------------+----------------------------------------+-----------------------+------+------+ |
| 197 | | time | type | union | enum | set | |
| 198 | +-------------------------------+----------------------------------------+-----------------------+------+------+ |
| 199 | | 1970-06-04 19:55:31 +0000 UTC | typeobject(map[float32]struct{B bool}) | Title: "dahar master" | Bad | {47} | |
| 200 | +-------------------------------+----------------------------------------+-----------------------+------+------+ |
| 201 | `, |
| 202 | }, |
| 203 | { |
| 204 | []string{"c1"}, |
| 205 | [][]interface{}{ |
| 206 | { |
| 207 | db.Recursive{nil, &db.Times{time.Unix(123456789, 42244224), time.Duration(13377331)}, map[db.Array2String]db.Recursive{ |
| 208 | db.Array2String{"a", "b"}: db.Recursive{}, |
| 209 | db.Array2String{"x\nx", "y\"y"}: db.Recursive{vdl.ValueOf(db.AgencyReportExperianReport{db.ExperianCreditReport{db.ExperianRatingGood}}), nil, nil}, |
| 210 | }}, |
| 211 | }, |
| 212 | }, |
| 213 | ` |
| 214 | +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ |
| 215 | | c1 | |
| 216 | +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ |
| 217 | | {Any: nil, Maybe: {Stamp: "1973-11-29 21:33:09.042244224 +0000 UTC", Interval: "13.377331ms"}, Rec: {["a", "b"]: {Any: nil, Maybe: nil, Rec: {}}, ["x\nx", "y\"y"]: {Any: ExperianReport: {Rating: Good}, Maybe: nil, Rec: {}}}} | |
| 218 | +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ |
| 219 | `, |
| 220 | }, |
| 221 | } |
| 222 | for _, test := range tests { |
| 223 | var b bytes.Buffer |
| 224 | if err := writer.WriteTable(&b, test.columns, newResultStream(test.rows)); err != nil { |
| 225 | t.Errorf("Unexpected error: %v", err) |
| 226 | continue |
| 227 | } |
| 228 | // Add a leading newline to the output to match the leading newline |
| 229 | // in our test cases. |
| 230 | if got, want := "\n"+b.String(), test.output; got != want { |
| 231 | t.Errorf("Wrong output:\nGOT:%s\nWANT:%s", got, want) |
| 232 | } |
| 233 | } |
| 234 | } |
| 235 | |
| 236 | func TestWriteCSV(t *testing.T) { |
| 237 | type testCase struct { |
| 238 | columns []string |
| 239 | rows [][]interface{} |
| 240 | delimiter string |
| 241 | // To make the test cases easier to read, output should have a leading |
| 242 | // newline. |
| 243 | output string |
| 244 | } |
| 245 | tests := []testCase{ |
| 246 | { // Basic. |
| 247 | []string{"c1", "c2"}, |
| 248 | [][]interface{}{ |
| 249 | {5, "foo"}, |
| 250 | {6, "bar"}, |
| 251 | }, |
| 252 | ",", |
| 253 | ` |
| 254 | c1,c2 |
| 255 | 5,foo |
| 256 | 6,bar |
| 257 | `, |
| 258 | }, |
| 259 | { // Numbers. |
| 260 | []string{"byte", "uint16", "uint32", "uint64", "int16", "int32", "int64", |
| 261 | "float32", "float64", "complex64", "complex128"}, |
| 262 | [][]interface{}{ |
| 263 | { |
| 264 | byte(12), uint16(1234), uint32(5678), uint64(999888777666), int16(9876), int32(876543), int64(128), |
| 265 | float32(3.14159), float64(2.71828182846), complex64(123.0 + 7.0i), complex128(456.789 + 10.1112i), |
| 266 | }, |
| 267 | { |
| 268 | byte(9), uint16(99), uint32(999), uint64(9999999), int16(9), int32(99), int64(88), |
| 269 | float32(1.41421356237), float64(1.73205080757), complex64(9.87 + 7.65i), complex128(4.32 + 1.0i), |
| 270 | }, |
| 271 | }, |
| 272 | ",", |
| 273 | ` |
| 274 | byte,uint16,uint32,uint64,int16,int32,int64,float32,float64,complex64,complex128 |
| 275 | 12,1234,5678,999888777666,9876,876543,128,3.141590118408203,2.71828182846,123+7i,456.789+10.1112i |
| 276 | 9,99,999,9999999,9,99,88,1.4142135381698608,1.73205080757,9.869999885559082+7.650000095367432i,4.32+1i |
| 277 | `, |
| 278 | }, |
| 279 | { |
| 280 | // Values containing newlines, double quotes, and the delimiter must be |
| 281 | // enclosed in double quotes. |
| 282 | []string{"c1", "c2"}, |
| 283 | [][]interface{}{ |
| 284 | {"foo\tbar", "foo\nbar"}, |
| 285 | {"foo\"bar\"", "foo,bar"}, |
| 286 | }, |
| 287 | ",", |
| 288 | ` |
| 289 | c1,c2 |
| 290 | foo bar,"foo |
| 291 | bar" |
| 292 | "foo""bar""","foo,bar" |
| 293 | `, |
| 294 | }, |
| 295 | { // Delimiters other than comma should be supported. |
| 296 | []string{"c1", "c2"}, |
| 297 | [][]interface{}{ |
| 298 | {"foo\tbar", "foo\nbar"}, |
| 299 | {"foo\"bar\"", "foo,bar"}, |
| 300 | }, |
| 301 | "\t", |
| 302 | ` |
| 303 | c1 c2 |
| 304 | "foo bar" "foo |
| 305 | bar" |
| 306 | "foo""bar""" foo,bar |
| 307 | `, |
| 308 | }, |
| 309 | { // Column names should be escaped properly. |
| 310 | []string{"foo\tbar", "foo,bar"}, |
| 311 | [][]interface{}{}, |
| 312 | ",", |
| 313 | ` |
| 314 | foo bar,"foo,bar" |
| 315 | `, |
| 316 | }, |
| 317 | { // Same as above but use a non-default delimiter. |
| 318 | []string{"foo\tbar", "foo,棎鶊鵱"}, |
| 319 | [][]interface{}{}, |
| 320 | "\t", |
| 321 | ` |
| 322 | "foo bar" foo,棎鶊鵱 |
| 323 | `, |
| 324 | }, |
| 325 | { |
| 326 | []string{"c1"}, |
| 327 | [][]interface{}{ |
| 328 | {db.Customer{"John Smith", 1, true, db.AddressInfo{"1 Main St.", "Palo Alto", "CA", "94303"}, db.CreditReport{Agency: db.CreditAgencyEquifax, Report: db.AgencyReportEquifaxReport{db.EquifaxCreditReport{'A'}}}}}, |
| 329 | {db.Invoice{1, 1000, 42, db.AddressInfo{"1 Main St.", "Palo Alto", "CA", "94303"}}}, |
| 330 | }, |
| 331 | ",", |
| 332 | ` |
| 333 | c1 |
| 334 | "{Name: ""John Smith"", Id: 1, Active: true, Address: {Street: ""1 Main St."", City: ""Palo Alto"", State: ""CA"", Zip: ""94303""}, Credit: {Agency: Equifax, Report: EquifaxReport: {Rating: 65}}}" |
| 335 | "{CustId: 1, InvoiceNum: 1000, Amount: 42, ShipTo: {Street: ""1 Main St."", City: ""Palo Alto"", State: ""CA"", Zip: ""94303""}}" |
| 336 | `, |
| 337 | }, |
| 338 | } |
| 339 | for _, test := range tests { |
| 340 | var b bytes.Buffer |
| 341 | if err := writer.WriteCSV(&b, test.columns, newResultStream(test.rows), test.delimiter); err != nil { |
| 342 | t.Errorf("Unexpected error: %v", err) |
| 343 | continue |
| 344 | } |
| 345 | // Add a leading newline to the output to match the leading newline |
| 346 | // in our test cases. |
| 347 | if got, want := "\n"+b.String(), test.output; got != want { |
| 348 | t.Errorf("Wrong output:\nGOT: %q\nWANT:%q", got, want) |
| 349 | } |
| 350 | } |
| 351 | } |
| 352 | |
| 353 | func TestWriteJson(t *testing.T) { |
| 354 | type testCase struct { |
| 355 | columns []string |
| 356 | rows [][]interface{} |
| 357 | // To make the test cases easier to read, output should have a leading |
| 358 | // newline. |
| 359 | output string |
| 360 | } |
| 361 | tests := []testCase{ |
| 362 | { // Basic. |
| 363 | []string{"c\n1", "c鶊2"}, |
| 364 | [][]interface{}{ |
| 365 | {5, "foo\nbar"}, |
| 366 | {6, "bar\tfoo"}, |
| 367 | }, |
| 368 | ` |
| 369 | [{ |
| 370 | "c\n1": 5, |
| 371 | "c鶊2": "foo\nbar" |
| 372 | }, { |
| 373 | "c\n1": 6, |
| 374 | "c鶊2": "bar\tfoo" |
| 375 | }] |
| 376 | `, |
| 377 | }, |
| 378 | { // Numbers. |
| 379 | []string{"byte", "uint16", "uint32", "uint64", "int16", "int32", "int64", |
| 380 | "float32", "float64", "complex64", "complex128"}, |
| 381 | [][]interface{}{ |
| 382 | { |
| 383 | byte(12), uint16(1234), uint32(5678), uint64(999888777666), int16(9876), int32(876543), int64(128), |
| 384 | float32(3.14159), float64(2.71828182846), complex64(123.0 + 7.0i), complex128(456.789 + 10.1112i), |
| 385 | }, |
| 386 | { |
| 387 | byte(9), uint16(99), uint32(999), uint64(9999999), int16(9), int32(99), int64(88), |
| 388 | float32(1.41421356237), float64(1.73205080757), complex64(9.87 + 7.65i), complex128(4.32 + 1.0i), |
| 389 | }, |
| 390 | }, |
| 391 | ` |
| 392 | [{ |
| 393 | "byte": 12, |
| 394 | "uint16": 1234, |
| 395 | "uint32": 5678, |
| 396 | "uint64": 999888777666, |
| 397 | "int16": 9876, |
| 398 | "int32": 876543, |
| 399 | "int64": 128, |
| 400 | "float32": 3.141590118408203, |
| 401 | "float64": 2.71828182846, |
| 402 | "complex64": "123+7i", |
| 403 | "complex128": "456.789+10.1112i" |
| 404 | }, { |
| 405 | "byte": 9, |
| 406 | "uint16": 99, |
| 407 | "uint32": 999, |
| 408 | "uint64": 9999999, |
| 409 | "int16": 9, |
| 410 | "int32": 99, |
| 411 | "int64": 88, |
| 412 | "float32": 1.4142135381698608, |
| 413 | "float64": 1.73205080757, |
| 414 | "complex64": "9.869999885559082+7.650000095367432i", |
| 415 | "complex128": "4.32+1i" |
| 416 | }] |
| 417 | `, |
| 418 | }, |
| 419 | { // Empty result. |
| 420 | []string{"nothing", "nada", "zilch"}, |
| 421 | [][]interface{}{}, |
| 422 | ` |
| 423 | [] |
| 424 | `, |
| 425 | }, |
| 426 | { // Empty column set. |
| 427 | []string{}, |
| 428 | [][]interface{}{ |
| 429 | {}, |
| 430 | {}, |
| 431 | }, |
| 432 | ` |
| 433 | [{ |
| 434 | }, { |
| 435 | }] |
| 436 | `, |
| 437 | }, |
| 438 | { // Empty values. |
| 439 | []string{"blank", "empty", "nil"}, |
| 440 | [][]interface{}{ |
| 441 | {struct{}{}, []string{}, nil}, |
| 442 | }, |
| 443 | ` |
| 444 | [{ |
| 445 | "blank": {}, |
| 446 | "empty": [], |
| 447 | "nil": null |
| 448 | }] |
| 449 | `, |
| 450 | }, |
| 451 | { |
| 452 | []string{"c1"}, |
| 453 | [][]interface{}{ |
| 454 | {db.Customer{"John Smith", 1, true, db.AddressInfo{"1 Main St.", "Palo Alto", "CA", "94303"}, db.CreditReport{Agency: db.CreditAgencyEquifax, Report: db.AgencyReportEquifaxReport{db.EquifaxCreditReport{'A'}}}}}, |
| 455 | {db.Invoice{1, 1000, 42, db.AddressInfo{"1 Main St.", "Palo Alto", "CA", "94303"}}}, |
| 456 | }, |
| 457 | ` |
| 458 | [{ |
| 459 | "c1": {"Name":"John Smith","Id":1,"Active":true,"Address":{"Street":"1 Main St.","City":"Palo Alto","State":"CA","Zip":"94303"},"Credit":{"Agency":"Equifax","Report":{"EquifaxReport":{"Rating":65}}}} |
| 460 | }, { |
| 461 | "c1": {"CustId":1,"InvoiceNum":1000,"Amount":42,"ShipTo":{"Street":"1 Main St.","City":"Palo Alto","State":"CA","Zip":"94303"}} |
| 462 | }] |
| 463 | `, |
| 464 | }, |
| 465 | { |
| 466 | []string{"nil", "composite", "typeobj"}, |
| 467 | [][]interface{}{ |
| 468 | { |
| 469 | nil, |
| 470 | db.Composite{db.Array2String{"foo", "bar"}, []int32{1, 2}, map[int32]struct{}{1: struct{}{}, 2: struct{}{}}, map[string]int32{"foo": 1, "bar": 2}}, |
| 471 | vdl.TypeOf(map[string]struct{}{}), |
| 472 | }, |
| 473 | }, |
| 474 | ` |
| 475 | [{ |
| 476 | "nil": null, |
| 477 | "composite": {"Arr":["foo","bar"],"ListInt":[1,2],"MySet":{"1":true,"2":true},"Map":{"bar":2,"foo":1}}, |
| 478 | "typeobj": "typeobject(set[string])" |
| 479 | }] |
| 480 | `, |
| 481 | }, |
| 482 | { |
| 483 | []string{"c1"}, |
| 484 | [][]interface{}{ |
| 485 | { |
| 486 | db.Recursive{nil, &db.Times{time.Unix(123456789, 42244224), time.Duration(1337)}, map[db.Array2String]db.Recursive{ |
| 487 | db.Array2String{"a", "棎鶊鵱"}: db.Recursive{}, |
| 488 | db.Array2String{"x", "y"}: db.Recursive{vdl.ValueOf(db.CreditReport{Agency: db.CreditAgencyExperian, Report: db.AgencyReportExperianReport{db.ExperianCreditReport{db.ExperianRatingGood}}}), nil, nil}, |
| 489 | }}, |
| 490 | }, |
| 491 | }, |
| 492 | ` |
| 493 | [{ |
| 494 | "c1": {"Any":null,"Maybe":{"Stamp":"1973-11-29 21:33:09.042244224 +0000 UTC","Interval":"1.337µs"},"Rec":{"[\"a\", \"棎鶊鵱\"]":{"Any":null,"Maybe":null,"Rec":{}},"[\"x\", \"y\"]":{"Any":{"Agency":"Experian","Report":{"ExperianReport":{"Rating":"Good"}}},"Maybe":null,"Rec":{}}}} |
| 495 | }] |
| 496 | `, |
| 497 | }, |
| 498 | } |
| 499 | for _, test := range tests { |
| 500 | var b bytes.Buffer |
| 501 | if err := writer.WriteJson(&b, test.columns, newResultStream(test.rows)); err != nil { |
| 502 | t.Errorf("Unexpected error: %v", err) |
| 503 | continue |
| 504 | } |
| 505 | var decoded interface{} |
| 506 | if err := json.Unmarshal(b.Bytes(), &decoded); err != nil { |
| 507 | t.Errorf("Got invalid JSON: %v", err) |
| 508 | } |
| 509 | // Add a leading newline to the output to match the leading newline |
| 510 | // in our test cases. |
| 511 | if got, want := "\n"+b.String(), test.output; got != want { |
| 512 | t.Errorf("Wrong output:\nGOT: %q\nWANT:%q", got, want) |
| 513 | } |
| 514 | } |
| 515 | } |