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  }