github.com/mithrandie/csvq@v1.18.1/lib/query/encode_test.go (about) 1 package query 2 3 import ( 4 "bytes" 5 "context" 6 "testing" 7 8 "github.com/mithrandie/csvq/lib/option" 9 "github.com/mithrandie/csvq/lib/value" 10 11 "github.com/mithrandie/go-text" 12 "github.com/mithrandie/go-text/json" 13 "github.com/mithrandie/ternary" 14 ) 15 16 var encodeViewTests = []struct { 17 Name string 18 View *View 19 Format option.Format 20 LineBreak text.LineBreak 21 WriteEncoding text.Encoding 22 WriteDelimiter rune 23 WriteDelimiterPositions []int 24 WriteAsSingleLine bool 25 WithoutHeader bool 26 EncloseAll bool 27 JsonEscape json.EscapeType 28 PrettyPrint bool 29 ScientificNotation bool 30 UseColor bool 31 Result string 32 Error string 33 }{ 34 { 35 Name: "Empty RecordSet", 36 View: &View{ 37 Header: NewHeader("test", []string{"c1", "c2\nsecond line", "c3"}), 38 RecordSet: []Record{}, 39 }, 40 Format: option.TEXT, 41 Error: "empty result set", 42 }, 43 { 44 Name: "Empty Fields", 45 View: &View{ 46 Header: NewHeader("", []string{}), 47 RecordSet: []Record{ 48 NewRecord([]value.Primary{}), 49 }, 50 }, 51 Format: option.TEXT, 52 Error: "empty result set", 53 }, 54 { 55 Name: "Text", 56 View: &View{ 57 Header: NewHeader("test", []string{"c1", "c2\nsecond line", "c3"}), 58 RecordSet: []Record{ 59 NewRecord([]value.Primary{value.NewInteger(-1), value.NewTernary(ternary.UNKNOWN), value.NewBoolean(true)}), 60 NewRecord([]value.Primary{value.NewFloat(2.0123), value.NewDatetimeFromString("2016-02-01T16:00:00.123456-07:00", nil, GetTestLocation()), value.NewString("abcdef")}), 61 NewRecord([]value.Primary{value.NewInteger(34567890), value.NewString(" abcdefghijklmnopqrstuvwxyzabcdefg\nhi\"jk日本語あアアA(\n"), value.NewNull()}), 62 }, 63 }, 64 Format: option.TEXT, 65 Result: "+----------+-------------------------------------+--------+\n" + 66 "| c1 | c2 | c3 |\n" + 67 "| | second line | |\n" + 68 "+----------+-------------------------------------+--------+\n" + 69 "| -1 | UNKNOWN | true |\n" + 70 "| 2.0123 | 2016-02-01T16:00:00.123456-07:00 | abcdef |\n" + 71 "| 34567890 | abcdefghijklmnopqrstuvwxyzabcdefg | NULL |\n" + 72 "| | hi\"jk日本語あアアA( | |\n" + 73 "| | | |\n" + 74 "+----------+-------------------------------------+--------+", 75 }, 76 { 77 Name: "Text, --without--header option is ignored", 78 View: &View{ 79 Header: NewHeader("test", []string{"c1", "c2\nsecond line", "c3"}), 80 RecordSet: []Record{ 81 NewRecord([]value.Primary{value.NewInteger(-1), value.NewTernary(ternary.UNKNOWN), value.NewBoolean(true)}), 82 NewRecord([]value.Primary{value.NewFloat(2.0123), value.NewDatetimeFromString("2016-02-01T16:00:00.123456-07:00", nil, GetTestLocation()), value.NewString("abcdef")}), 83 NewRecord([]value.Primary{value.NewInteger(34567890), value.NewString(" abcdefghijklmnopqrstuvwxyzabcdefg\nhi\"jk日本語あアアA(\n"), value.NewNull()}), 84 }, 85 }, 86 Format: option.TEXT, 87 WithoutHeader: true, 88 Result: "+----------+-------------------------------------+--------+\n" + 89 "| c1 | c2 | c3 |\n" + 90 "| | second line | |\n" + 91 "+----------+-------------------------------------+--------+\n" + 92 "| -1 | UNKNOWN | true |\n" + 93 "| 2.0123 | 2016-02-01T16:00:00.123456-07:00 | abcdef |\n" + 94 "| 34567890 | abcdefghijklmnopqrstuvwxyzabcdefg | NULL |\n" + 95 "| | hi\"jk日本語あアアA( | |\n" + 96 "| | | |\n" + 97 "+----------+-------------------------------------+--------+", 98 }, 99 { 100 Name: "Text with colors", 101 View: &View{ 102 Header: NewHeader("test", []string{"c1", "c2"}), 103 RecordSet: []Record{ 104 NewRecord([]value.Primary{value.NewInteger(-1), value.NewString("abcde")}), 105 NewRecord([]value.Primary{value.NewFloat(2.0123), value.NewString("abcdef\r\nghijkl")}), 106 }, 107 }, 108 Format: option.TEXT, 109 UseColor: true, 110 Result: "" + 111 "+--------+--------+\n" + 112 "| c1 | c2 |\n" + 113 "+--------+--------+\n" + 114 "| \033[35m-1\033[0m | \033[32mabcde\033[0m |\n" + 115 "| \033[35m2.0123\033[0m | \033[32mabcdef\033[0m |\n" + 116 "| | \033[32mghijkl\033[0m |\n" + 117 "+--------+--------+", 118 }, 119 { 120 Name: "Text using Scientific Notation", 121 View: &View{ 122 Header: NewHeader("test", []string{"c1"}), 123 RecordSet: []Record{ 124 NewRecord([]value.Primary{value.NewFloat(0.00000123)}), 125 }, 126 }, 127 Format: option.TEXT, 128 ScientificNotation: true, 129 Result: "+----------+\n" + 130 "| c1 |\n" + 131 "+----------+\n" + 132 "| 1.23e-06 |\n" + 133 "+----------+", 134 }, 135 { 136 Name: "Box", 137 View: &View{ 138 Header: NewHeader("test", []string{"c1", "c2\nsecond line", "c3"}), 139 RecordSet: []Record{ 140 NewRecord([]value.Primary{value.NewInteger(-1), value.NewTernary(ternary.UNKNOWN), value.NewBoolean(true)}), 141 NewRecord([]value.Primary{value.NewFloat(2.0123), value.NewDatetimeFromString("2016-02-01T16:00:00.123456-07:00", nil, GetTestLocation()), value.NewString("abcdef")}), 142 NewRecord([]value.Primary{value.NewInteger(34567890), value.NewString(" abcdefghijklmnopqrstuvwxyzabcdefg\nhi\"jk日本語あアアA(\n"), value.NewNull()}), 143 }, 144 }, 145 Format: option.BOX, 146 Result: "┌──────────┬─────────────────────────────────────┬────────┐\n" + 147 "│ c1 │ c2 │ c3 │\n" + 148 "│ │ second line │ │\n" + 149 "├──────────┼─────────────────────────────────────┼────────┤\n" + 150 "│ -1 │ UNKNOWN │ true │\n" + 151 "│ 2.0123 │ 2016-02-01T16:00:00.123456-07:00 │ abcdef │\n" + 152 "│ 34567890 │ abcdefghijklmnopqrstuvwxyzabcdefg │ NULL │\n" + 153 "│ │ hi\"jk日本語あアアA( │ │\n" + 154 "│ │ │ │\n" + 155 "└──────────┴─────────────────────────────────────┴────────┘", 156 }, 157 { 158 Name: "Box, --without-header option is ignored", 159 View: &View{ 160 Header: NewHeader("test", []string{"c1", "c2\nsecond line", "c3"}), 161 RecordSet: []Record{ 162 NewRecord([]value.Primary{value.NewInteger(-1), value.NewTernary(ternary.UNKNOWN), value.NewBoolean(true)}), 163 NewRecord([]value.Primary{value.NewFloat(2.0123), value.NewDatetimeFromString("2016-02-01T16:00:00.123456-07:00", nil, GetTestLocation()), value.NewString("abcdef")}), 164 NewRecord([]value.Primary{value.NewInteger(34567890), value.NewString(" abcdefghijklmnopqrstuvwxyzabcdefg\nhi\"jk日本語あアアA(\n"), value.NewNull()}), 165 }, 166 }, 167 Format: option.BOX, 168 WithoutHeader: true, 169 Result: "┌──────────┬─────────────────────────────────────┬────────┐\n" + 170 "│ c1 │ c2 │ c3 │\n" + 171 "│ │ second line │ │\n" + 172 "├──────────┼─────────────────────────────────────┼────────┤\n" + 173 "│ -1 │ UNKNOWN │ true │\n" + 174 "│ 2.0123 │ 2016-02-01T16:00:00.123456-07:00 │ abcdef │\n" + 175 "│ 34567890 │ abcdefghijklmnopqrstuvwxyzabcdefg │ NULL │\n" + 176 "│ │ hi\"jk日本語あアアA( │ │\n" + 177 "│ │ │ │\n" + 178 "└──────────┴─────────────────────────────────────┴────────┘", 179 }, 180 { 181 Name: "Box with colors", 182 View: &View{ 183 Header: NewHeader("test", []string{"c1", "c2"}), 184 RecordSet: []Record{ 185 NewRecord([]value.Primary{value.NewInteger(-1), value.NewString("abcde")}), 186 NewRecord([]value.Primary{value.NewFloat(2.0123), value.NewString("abcdef\r\nghijkl")}), 187 }, 188 }, 189 Format: option.BOX, 190 UseColor: true, 191 Result: "" + 192 "┌────────┬────────┐\n" + 193 "│ c1 │ c2 │\n" + 194 "├────────┼────────┤\n" + 195 "│ \033[35m-1\033[0m │ \033[32mabcde\033[0m │\n" + 196 "│ \033[35m2.0123\033[0m │ \033[32mabcdef\033[0m │\n" + 197 "│ │ \033[32mghijkl\033[0m │\n" + 198 "└────────┴────────┘", 199 }, 200 { 201 Name: "Fixed-Length Format", 202 View: &View{ 203 Header: NewHeader("test", []string{"c1", "c2", "c3"}), 204 RecordSet: []Record{ 205 NewRecord([]value.Primary{value.NewInteger(-1), value.NewTernary(ternary.UNKNOWN), value.NewBoolean(false)}), 206 NewRecord([]value.Primary{value.NewFloat(2.0123), value.NewDatetimeFromString("2016-02-01T16:00:00.123456-07:00", nil, GetTestLocation()), value.NewString("abcdef")}), 207 }, 208 }, 209 Format: option.FIXED, 210 WriteDelimiterPositions: []int{10, 42, 50}, 211 Result: "" + 212 "c1 c2 c3 \n" + 213 " -1 false \n" + 214 " 2.01232016-02-01T16:00:00.123456-07:00abcdef ", 215 }, 216 { 217 Name: "Fixed-Length Format Single Line", 218 View: &View{ 219 Header: NewHeader("test", []string{"c1", "c2", "c3"}), 220 RecordSet: []Record{ 221 NewRecord([]value.Primary{value.NewInteger(-1), value.NewTernary(ternary.UNKNOWN), value.NewBoolean(false)}), 222 NewRecord([]value.Primary{value.NewFloat(2.0123), value.NewDatetimeFromString("2016-02-01T16:00:00.123456-07:00", nil, GetTestLocation()), value.NewString("abcdef")}), 223 }, 224 }, 225 Format: option.FIXED, 226 WriteDelimiterPositions: []int{10, 42, 50}, 227 WriteAsSingleLine: true, 228 Result: "" + 229 " -1 false " + 230 " 2.01232016-02-01T16:00:00.123456-07:00abcdef ", 231 }, 232 { 233 Name: "Fixed-Length Format Auto Filled", 234 View: &View{ 235 Header: NewHeader("test", []string{"c1", "c2", "c3"}), 236 RecordSet: []Record{ 237 NewRecord([]value.Primary{value.NewInteger(-1), value.NewTernary(ternary.UNKNOWN), value.NewBoolean(false)}), 238 NewRecord([]value.Primary{value.NewFloat(2.0123), value.NewDatetimeFromString("2016-02-01T16:00:00.123456-07:00", nil, GetTestLocation()), value.NewString("abcdef")}), 239 }, 240 }, 241 Format: option.FIXED, 242 WriteDelimiterPositions: nil, 243 Result: "" + 244 "c1 c2 c3 \n" + 245 " -1 false \n" + 246 "2.0123 2016-02-01T16:00:00.123456-07:00 abcdef", 247 }, 248 { 249 Name: "Fixed-Length Format Data Empty", 250 View: &View{ 251 Header: NewHeader("test", []string{"c1", "c2", "c3"}), 252 RecordSet: []Record{}, 253 }, 254 Format: option.FIXED, 255 WriteDelimiterPositions: []int{10, 42, 50}, 256 WithoutHeader: true, 257 Error: "data empty", 258 }, 259 { 260 Name: "Fixed-Length Format Auto Filled Data Empty", 261 View: &View{ 262 Header: NewHeader("test", []string{"c1", "c2", "c3"}), 263 RecordSet: []Record{}, 264 }, 265 Format: option.FIXED, 266 WriteDelimiterPositions: nil, 267 WithoutHeader: true, 268 Error: "data empty", 269 }, 270 { 271 Name: "GFM LineBreak CRLF", 272 View: &View{ 273 Header: NewHeader("test", []string{"c1", "c2\nsecond line", "c3"}), 274 RecordSet: []Record{ 275 NewRecord([]value.Primary{value.NewFloat(2.0123), value.NewDatetimeFromString("2016-02-01T16:00:00.123456-07:00", nil, GetTestLocation()), value.NewString("abcdef")}), 276 NewRecord([]value.Primary{value.NewInteger(34567890), value.NewString(" ab|cdefghijklmnopqrstuvwxyzabcdefg\nhi\"jk日本語あアアA(\n"), value.NewNull()}), 277 }, 278 }, 279 Format: option.GFM, 280 LineBreak: text.CRLF, 281 Result: "" + 282 "| c1 | c2<br />second line | c3 |\r\n" + 283 "| -------: | --------------------------------------------------------------------- | ------ |\r\n" + 284 "| 2.0123 | 2016-02-01T16:00:00.123456-07:00 | abcdef |\r\n" + 285 "| 34567890 | ab\\|cdefghijklmnopqrstuvwxyzabcdefg<br />hi\"jk日本語あアアA(<br /> | |", 286 }, 287 { 288 Name: "GFM Data Empty", 289 View: &View{ 290 Header: NewHeader("test", []string{"c1", "c2\nsecond line", "c3"}), 291 RecordSet: []Record{}, 292 }, 293 Format: option.GFM, 294 WithoutHeader: true, 295 Error: "data empty", 296 }, 297 { 298 Name: "Org-mode Table", 299 View: &View{ 300 Header: NewHeader("test", []string{"c1", "c2\nsecond line", "c3"}), 301 RecordSet: []Record{ 302 NewRecord([]value.Primary{value.NewFloat(2.0123), value.NewDatetimeFromString("2016-02-01T16:00:00.123456-07:00", nil, GetTestLocation()), value.NewString("abcdef")}), 303 NewRecord([]value.Primary{value.NewInteger(34567890), value.NewString(" ab|cdefghijklmnopqrstuvwxyzabcdefg\nhi\"jk日本語あアアA(\n"), value.NewNull()}), 304 }, 305 }, 306 Format: option.ORG, 307 LineBreak: text.LF, 308 Result: "" + 309 "| c1 | c2<br />second line | c3 |\n" + 310 "|----------+-----------------------------------------------------------------------+--------|\n" + 311 "| 2.0123 | 2016-02-01T16:00:00.123456-07:00 | abcdef |\n" + 312 "| 34567890 | ab\\|cdefghijklmnopqrstuvwxyzabcdefg<br />hi\"jk日本語あアアA(<br /> | |", 313 }, 314 { 315 Name: "Org-mode Table Data Empty", 316 View: &View{ 317 Header: NewHeader("test", []string{"c1", "c2\nsecond line", "c3"}), 318 RecordSet: []Record{}, 319 }, 320 Format: option.ORG, 321 WithoutHeader: true, 322 Error: "data empty", 323 }, 324 { 325 Name: "TSV", 326 View: &View{ 327 Header: NewHeader("test", []string{"c1", "c2\nsecond line", "c3"}), 328 RecordSet: []Record{ 329 NewRecord([]value.Primary{value.NewInteger(-1), value.NewTernary(ternary.UNKNOWN), value.NewBoolean(true)}), 330 NewRecord([]value.Primary{value.NewFloat(2.0123), value.NewDatetimeFromString("2016-02-01T16:00:00.123456-07:00", nil, GetTestLocation()), value.NewString("abcdef")}), 331 NewRecord([]value.Primary{value.NewInteger(34567890), value.NewString(" abcdefghijklmnopqrstuvwxyzabcdefg\nhi\"jk\n"), value.NewNull()}), 332 }, 333 }, 334 Format: option.TSV, 335 WriteDelimiter: '\t', 336 EncloseAll: true, 337 Result: "\"c1\"\t\"c2\nsecond line\"\t\"c3\"\n" + 338 "-1\t\ttrue\n" + 339 "2.0123\t\"2016-02-01T16:00:00.123456-07:00\"\t\"abcdef\"\n" + 340 "34567890\t\" abcdefghijklmnopqrstuvwxyzabcdefg\nhi\"\"jk\n\"\t", 341 }, 342 { 343 Name: "TSV", 344 View: &View{ 345 Header: NewHeader("test", []string{"c1", "c2\nsecond line", "c3"}), 346 RecordSet: []Record{}, 347 }, 348 Format: option.TSV, 349 WriteDelimiter: '\t', 350 WithoutHeader: true, 351 Error: "data empty", 352 }, 353 { 354 Name: "CSV WithoutHeader", 355 View: &View{ 356 Header: NewHeader("test", []string{"c1", "c2\nsecond line", "c3"}), 357 RecordSet: []Record{ 358 NewRecord([]value.Primary{value.NewInteger(-1), value.NewTernary(ternary.UNKNOWN), value.NewBoolean(true)}), 359 NewRecord([]value.Primary{value.NewFloat(2.0123), value.NewDatetimeFromString("2016-02-01T16:00:00.123456-07:00", nil, GetTestLocation()), value.NewString("abcdef")}), 360 NewRecord([]value.Primary{value.NewInteger(34567890), value.NewString(" abcdefghijklmnopqrstuvwxyzabcdefg\nhi\"jk\n"), value.NewNull()}), 361 }, 362 }, 363 Format: option.CSV, 364 WithoutHeader: true, 365 EncloseAll: true, 366 Result: "-1,,true\n" + 367 "2.0123,\"2016-02-01T16:00:00.123456-07:00\",\"abcdef\"\n" + 368 "34567890,\" abcdefghijklmnopqrstuvwxyzabcdefg\nhi\"\"jk\n\",", 369 }, 370 { 371 Name: "CSV Line Break CRLF", 372 View: &View{ 373 Header: NewHeader("test", []string{"c1", "c2\nsecond line", "c3"}), 374 RecordSet: []Record{ 375 NewRecord([]value.Primary{value.NewInteger(-1), value.NewTernary(ternary.UNKNOWN), value.NewBoolean(true)}), 376 NewRecord([]value.Primary{value.NewFloat(2.0123), value.NewDatetimeFromString("2016-02-01T16:00:00.123456-07:00", nil, GetTestLocation()), value.NewString("abcdef")}), 377 NewRecord([]value.Primary{value.NewInteger(34567890), value.NewString(" abcdefghijklmnopqrstuvwxyzabcdefg\nhi\"jk\n"), value.NewNull()}), 378 }, 379 }, 380 Format: option.CSV, 381 LineBreak: text.CRLF, 382 EncloseAll: true, 383 Result: "\"c1\",\"c2\nsecond line\",\"c3\"\r\n" + 384 "-1,,true\r\n" + 385 "2.0123,\"2016-02-01T16:00:00.123456-07:00\",\"abcdef\"\r\n" + 386 "34567890,\" abcdefghijklmnopqrstuvwxyzabcdefg\nhi\"\"jk\n\",", 387 }, 388 { 389 Name: "JSON", 390 View: &View{ 391 Header: NewHeader("test", []string{"c1", "c2\nsecond line", "c3"}), 392 RecordSet: []Record{ 393 NewRecord([]value.Primary{value.NewInteger(-1), value.NewTernary(ternary.UNKNOWN), value.NewBoolean(true)}), 394 NewRecord([]value.Primary{value.NewInteger(-1), value.NewTernary(ternary.FALSE), value.NewBoolean(true)}), 395 NewRecord([]value.Primary{value.NewFloat(2.0123), value.NewDatetimeFromString("2016-02-01T16:00:00.123456-07:00", nil, GetTestLocation()), value.NewString("abcdef")}), 396 NewRecord([]value.Primary{value.NewInteger(34567890), value.NewString(" abc\\defghi/jk\rlmn\topqrstuvwxyzabcdefg\nhi\"jk\n"), value.NewNull()}), 397 }, 398 }, 399 Format: option.JSON, 400 Result: "[" + 401 "{" + 402 "\"c1\":-1," + 403 "\"c2\\nsecond line\":null," + 404 "\"c3\":true" + 405 "}," + 406 "{" + 407 "\"c1\":-1," + 408 "\"c2\\nsecond line\":false," + 409 "\"c3\":true" + 410 "}," + 411 "{" + 412 "\"c1\":2.0123," + 413 "\"c2\\nsecond line\":\"2016-02-01T16:00:00.123456-07:00\"," + 414 "\"c3\":\"abcdef\"" + 415 "}," + 416 "{" + 417 "\"c1\":34567890," + 418 "\"c2\\nsecond line\":\" abc\\\\defghi\\/jk\\rlmn\\topqrstuvwxyzabcdefg\\nhi\\\"jk\\n\"," + 419 "\"c3\":null" + 420 "}" + 421 "]", 422 }, 423 { 424 Name: "JSONH", 425 View: &View{ 426 Header: NewHeader("test", []string{"c1"}), 427 RecordSet: []Record{ 428 NewRecord([]value.Primary{value.NewString("a")}), 429 NewRecord([]value.Primary{value.NewString("b")}), 430 NewRecord([]value.Primary{value.NewString("abc\\def")}), 431 }, 432 }, 433 Format: option.JSON, 434 JsonEscape: json.HexDigits, 435 Result: "[" + 436 "{" + 437 "\"c1\":\"a\"" + 438 "}," + 439 "{" + 440 "\"c1\":\"b\"" + 441 "}," + 442 "{" + 443 "\"c1\":\"abc\\u005cdef\"" + 444 "}" + 445 "]", 446 }, 447 { 448 Name: "JSONA", 449 View: &View{ 450 Header: NewHeader("test", []string{"c1"}), 451 RecordSet: []Record{ 452 NewRecord([]value.Primary{value.NewString("a")}), 453 NewRecord([]value.Primary{value.NewString("b")}), 454 NewRecord([]value.Primary{value.NewString("abc\\def")}), 455 }, 456 }, 457 Format: option.JSON, 458 JsonEscape: json.AllWithHexDigits, 459 Result: "[" + 460 "{" + 461 "\"\\u0063\\u0031\":\"\\u0061\"" + 462 "}," + 463 "{" + 464 "\"\\u0063\\u0031\":\"\\u0062\"" + 465 "}," + 466 "{" + 467 "\"\\u0063\\u0031\":\"\\u0061\\u0062\\u0063\\u005c\\u0064\\u0065\\u0066\"" + 468 "}" + 469 "]", 470 }, 471 { 472 Name: "JSONH Pretty Print", 473 View: &View{ 474 Header: NewHeader("test", []string{"c1"}), 475 RecordSet: []Record{ 476 NewRecord([]value.Primary{value.NewString("a")}), 477 NewRecord([]value.Primary{value.NewString("b")}), 478 NewRecord([]value.Primary{value.NewString("abc\\def")}), 479 }, 480 }, 481 Format: option.JSON, 482 JsonEscape: json.HexDigits, 483 PrettyPrint: true, 484 Result: "[\n" + 485 " {\n" + 486 " \"c1\": \"a\"\n" + 487 " },\n" + 488 " {\n" + 489 " \"c1\": \"b\"\n" + 490 " },\n" + 491 " {\n" + 492 " \"c1\": \"abc\\u005cdef\"\n" + 493 " }\n" + 494 "]", 495 }, 496 { 497 Name: "JSON using Scientific Notation", 498 View: &View{ 499 Header: NewHeader("test", []string{"c1"}), 500 RecordSet: []Record{ 501 NewRecord([]value.Primary{value.NewFloat(0.00000123)}), 502 }, 503 }, 504 Format: option.JSON, 505 ScientificNotation: true, 506 Result: "[" + 507 "{" + 508 "\"c1\":1.23e-06" + 509 "}" + 510 "]", 511 }, 512 { 513 Name: "JSONL", 514 View: &View{ 515 Header: NewHeader("test", []string{"c1", "c2\nsecond line", "c3"}), 516 RecordSet: []Record{ 517 NewRecord([]value.Primary{value.NewInteger(-1), value.NewTernary(ternary.UNKNOWN), value.NewBoolean(true)}), 518 NewRecord([]value.Primary{value.NewInteger(-1), value.NewTernary(ternary.FALSE), value.NewBoolean(true)}), 519 NewRecord([]value.Primary{value.NewFloat(2.0123), value.NewDatetimeFromString("2016-02-01T16:00:00.123456-07:00", nil, GetTestLocation()), value.NewString("abcdef")}), 520 NewRecord([]value.Primary{value.NewInteger(34567890), value.NewString(" abc\\defghi/jk\rlmn\topqrstuvwxyzabcdefg\nhi\"jk\n"), value.NewNull()}), 521 }, 522 }, 523 Format: option.JSONL, 524 Result: "{" + 525 "\"c1\":-1," + 526 "\"c2\\nsecond line\":null," + 527 "\"c3\":true" + 528 "}\n" + 529 "{" + 530 "\"c1\":-1," + 531 "\"c2\\nsecond line\":false," + 532 "\"c3\":true" + 533 "}\n" + 534 "{" + 535 "\"c1\":2.0123," + 536 "\"c2\\nsecond line\":\"2016-02-01T16:00:00.123456-07:00\"," + 537 "\"c3\":\"abcdef\"" + 538 "}\n" + 539 "{" + 540 "\"c1\":34567890," + 541 "\"c2\\nsecond line\":\" abc\\\\defghi\\/jk\\rlmn\\topqrstuvwxyzabcdefg\\nhi\\\"jk\\n\"," + 542 "\"c3\":null" + 543 "}\n", 544 }, 545 { 546 Name: "JSONL using Scientific Notation", 547 View: &View{ 548 Header: NewHeader("test", []string{"c1"}), 549 RecordSet: []Record{ 550 NewRecord([]value.Primary{value.NewFloat(0.00000123)}), 551 }, 552 }, 553 Format: option.JSONL, 554 ScientificNotation: true, 555 Result: "{" + 556 "\"c1\":1.23e-06" + 557 "}\n", 558 }, 559 { 560 Name: "LTSV", 561 View: &View{ 562 Header: NewHeader("test", []string{"c1", "c2", "c3"}), 563 RecordSet: []Record{ 564 NewRecord([]value.Primary{value.NewInteger(-1), value.NewTernary(ternary.FALSE), value.NewBoolean(true)}), 565 NewRecord([]value.Primary{value.NewFloat(2.0123), value.NewDatetimeFromString("2016-02-01T16:00:00.123456-07:00", nil, GetTestLocation()), value.NewString("abcdef")}), 566 }, 567 }, 568 Format: option.LTSV, 569 Result: "c1:-1\tc2:false\tc3:true\n" + 570 "c1:2.0123\tc2:2016-02-01T16:00:00.123456-07:00\tc3:abcdef", 571 }, 572 { 573 Name: "LTSV Data Empty", 574 View: &View{ 575 Header: NewHeader("test", []string{"c1", "c2", "c3"}), 576 RecordSet: []Record{}, 577 }, 578 Format: option.LTSV, 579 Error: "data empty", 580 }, 581 { 582 Name: "Fixed-Length Format Invalid Positions", 583 View: &View{ 584 Header: NewHeader("test", []string{"c1", "c2", "c3"}), 585 RecordSet: []Record{ 586 NewRecord([]value.Primary{value.NewInteger(-1), value.NewTernary(ternary.UNKNOWN), value.NewBoolean(false)}), 587 NewRecord([]value.Primary{value.NewFloat(2.0123), value.NewDatetimeFromString("2016-02-01T16:00:00.123456-07:00", nil, GetTestLocation()), value.NewString("abcdef")}), 588 }, 589 }, 590 Format: option.FIXED, 591 WriteDelimiterPositions: []int{10, 42, -1}, 592 Error: "data encode error: invalid delimiter position: [10, 42, -1]", 593 }, 594 { 595 Name: "JSONH Column Name Convert Error", 596 View: &View{ 597 Header: NewHeader("test", []string{"c1.."}), 598 RecordSet: []Record{ 599 NewRecord([]value.Primary{value.NewString("a")}), 600 NewRecord([]value.Primary{value.NewString("b")}), 601 NewRecord([]value.Primary{value.NewString("abc\\def")}), 602 }, 603 }, 604 Format: option.JSON, 605 JsonEscape: json.HexDigits, 606 Error: "data encode error: unexpected token \".\" at column 4 in \"c1..\"", 607 }, 608 { 609 Name: "LTSV Invalid Character in Label", 610 View: &View{ 611 Header: NewHeader("test", []string{"c1:", "c2", "c3"}), 612 RecordSet: []Record{ 613 NewRecord([]value.Primary{value.NewInteger(-1), value.NewTernary(ternary.FALSE), value.NewBoolean(true)}), 614 NewRecord([]value.Primary{value.NewFloat(2.0123), value.NewDatetimeFromString("2016-02-01T16:00:00.123456-07:00", nil, GetTestLocation()), value.NewString("abcdef")}), 615 }, 616 }, 617 Format: option.LTSV, 618 Error: "data encode error: unpermitted character in label: U+003A", 619 }, 620 { 621 Name: "LTSV Invalid Character in Field-Value", 622 View: &View{ 623 Header: NewHeader("test", []string{"c1", "c2", "c3"}), 624 RecordSet: []Record{ 625 NewRecord([]value.Primary{value.NewInteger(-1), value.NewTernary(ternary.FALSE), value.NewBoolean(true)}), 626 NewRecord([]value.Primary{value.NewFloat(2.0123), value.NewDatetimeFromString("2016-02-01T16:00:00.123456-07:00", nil, GetTestLocation()), value.NewString("abc\tdef")}), 627 }, 628 }, 629 Format: option.LTSV, 630 Error: "data encode error: unpermitted character in field-value: U+0009", 631 }, 632 { 633 Name: "CSV Encode Character Code", 634 View: &View{ 635 Header: NewHeader("test", []string{"c1", "c2\nsecond line", "c3"}), 636 RecordSet: []Record{ 637 NewRecord([]value.Primary{value.NewInteger(-1), value.NewTernary(ternary.UNKNOWN), value.NewBoolean(true)}), 638 NewRecord([]value.Primary{value.NewInteger(-1), value.NewTernary(ternary.FALSE), value.NewBoolean(true)}), 639 NewRecord([]value.Primary{value.NewFloat(2.0123), value.NewDatetimeFromString("2016-02-01T16:00:00.123456-07:00", nil, GetTestLocation()), value.NewString("abcdef")}), 640 NewRecord([]value.Primary{value.NewInteger(34567890), value.NewString(" 日本語ghijklmnopqrstuvwxyzabcdefg\nhi\"jk\n"), value.NewNull()}), 641 }, 642 }, 643 Format: option.CSV, 644 WriteEncoding: text.SJIS, 645 EncloseAll: true, 646 Result: "\"c1\",\"c2\nsecond line\",\"c3\"\n" + 647 "-1,,true\n" + 648 "-1,false,true\n" + 649 "2.0123,\"2016-02-01T16:00:00.123456-07:00\",\"abcdef\"\n" + 650 "34567890,\" " + string([]byte{0x93, 0xfa, 0x96, 0x7b, 0x8c, 0xea}) + "ghijklmnopqrstuvwxyzabcdefg\nhi\"\"jk\n\",", 651 }, 652 } 653 654 func TestEncodeView(t *testing.T) { 655 defer TestTx.UseColor(false) 656 657 buf := &bytes.Buffer{} 658 ctx := context.Background() 659 660 for _, v := range encodeViewTests { 661 if v.WriteEncoding == text.AUTO { 662 v.WriteEncoding = text.UTF8 663 } 664 if v.LineBreak == "" { 665 v.LineBreak = text.LF 666 } 667 if v.WriteDelimiter == 0 { 668 v.WriteDelimiter = ',' 669 } 670 TestTx.UseColor(v.UseColor) 671 672 options := TestTx.Flags.ExportOptions.Copy() 673 options.Format = v.Format 674 options.Delimiter = v.WriteDelimiter 675 options.DelimiterPositions = v.WriteDelimiterPositions 676 options.Encoding = v.WriteEncoding 677 options.LineBreak = v.LineBreak 678 options.WithoutHeader = v.WithoutHeader 679 options.EncloseAll = v.EncloseAll 680 options.JsonEscape = v.JsonEscape 681 options.PrettyPrint = v.PrettyPrint 682 options.ScientificNotation = v.ScientificNotation 683 options.SingleLine = v.WriteAsSingleLine 684 685 buf.Reset() 686 _, err := EncodeView(ctx, buf, v.View, options, TestTx.Palette) 687 if err != nil { 688 if len(v.Error) < 1 { 689 t.Errorf("%s: unexpected error %q", v.Name, err) 690 } else if err.Error() != v.Error { 691 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 692 } 693 continue 694 } 695 if 0 < len(v.Error) { 696 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 697 continue 698 } 699 700 result := buf.String() 701 if result != v.Result { 702 t.Errorf("%s: result = %s, want %s", v.Name, result, v.Result) 703 } 704 } 705 }