go-hep.org/x/hep@v0.38.1/csvutil/csv_test.go (about)

     1  // Copyright ©2016 The go-hep 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 csvutil_test
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  	"testing"
    11  
    12  	"go-hep.org/x/hep/csvutil"
    13  	"go-hep.org/x/hep/internal/diff"
    14  )
    15  
    16  func TestCSVReaderScanArgs(t *testing.T) {
    17  	fname := "testdata/simple.csv"
    18  	tbl, err := csvutil.Open(fname)
    19  	if err != nil {
    20  		t.Errorf("could not open %s: %+v\n", fname, err)
    21  	}
    22  	defer tbl.Close()
    23  	tbl.Reader.Comma = ';'
    24  	tbl.Reader.Comment = '#'
    25  
    26  	rows, err := tbl.ReadRows(0, 10)
    27  	if err != nil {
    28  		t.Errorf("could read rows [0, 10): %+v\n", err)
    29  	}
    30  	defer rows.Close()
    31  
    32  	irow := 0
    33  	for rows.Next() {
    34  		var (
    35  			i int
    36  			f float64
    37  			s string
    38  		)
    39  		err = rows.Scan(&i, &f, &s)
    40  		if err != nil {
    41  			t.Errorf("error reading row %d: %+v\n", irow, err)
    42  		}
    43  		exp := fmt.Sprintf("%d;%d;str-%d", irow, irow, irow)
    44  		got := fmt.Sprintf("%v;%v;%v", i, f, s)
    45  		if exp != got {
    46  			t.Errorf("error reading row %d\nexp=%q\ngot=%q\n",
    47  				irow, exp, got,
    48  			)
    49  		}
    50  		irow++
    51  	}
    52  
    53  	err = rows.Err()
    54  	if err != nil {
    55  		t.Errorf("error iterating over rows: %+v\n", err)
    56  	}
    57  }
    58  
    59  func TestCSVReaderScanStruct(t *testing.T) {
    60  	fname := "testdata/simple.csv"
    61  	tbl, err := csvutil.Open(fname)
    62  	if err != nil {
    63  		t.Errorf("could not open %s: %+v\n", fname, err)
    64  	}
    65  	defer tbl.Close()
    66  	tbl.Reader.Comma = ';'
    67  	tbl.Reader.Comment = '#'
    68  
    69  	rows, err := tbl.ReadRows(0, 10)
    70  	if err != nil {
    71  		t.Errorf("could read rows [0, 10): %+v\n", err)
    72  	}
    73  	defer rows.Close()
    74  
    75  	irow := 0
    76  	for rows.Next() {
    77  		data := struct {
    78  			I int
    79  			F float64
    80  			S string
    81  		}{}
    82  		err = rows.Scan(&data)
    83  		if err != nil {
    84  			t.Errorf("error reading row %d: %+v\n", irow, err)
    85  		}
    86  		exp := fmt.Sprintf("%d;%d;str-%d", irow, irow, irow)
    87  		got := fmt.Sprintf("%v;%v;%v", data.I, data.F, data.S)
    88  		if exp != got {
    89  			t.Errorf("error reading row %d\nexp=%q\ngot=%q\n",
    90  				irow, exp, got,
    91  			)
    92  		}
    93  		irow++
    94  	}
    95  
    96  	err = rows.Err()
    97  	if err != nil {
    98  		t.Errorf("error iterating over rows: %+v\n", err)
    99  	}
   100  }
   101  
   102  func TestCSVReaderScanSmallRead(t *testing.T) {
   103  	fname := "testdata/simple.csv"
   104  	tbl, err := csvutil.Open(fname)
   105  	if err != nil {
   106  		t.Errorf("could not open %s: %+v\n", fname, err)
   107  	}
   108  	defer tbl.Close()
   109  	tbl.Reader.Comma = ';'
   110  	tbl.Reader.Comment = '#'
   111  
   112  	rows, err := tbl.ReadRows(0, 2)
   113  	if err != nil {
   114  		t.Errorf("could read rows [0, 2): %+v\n", err)
   115  	}
   116  	defer rows.Close()
   117  
   118  	irow := 0
   119  	for rows.Next() {
   120  		data := struct {
   121  			I int
   122  			F float64
   123  			S string
   124  		}{}
   125  		err = rows.Scan(&data)
   126  		if err != nil {
   127  			t.Errorf("error reading row %d: %+v\n", irow, err)
   128  		}
   129  		exp := fmt.Sprintf("%d;%d;str-%d", irow, irow, irow)
   130  		got := fmt.Sprintf("%v;%v;%v", data.I, data.F, data.S)
   131  		if exp != got {
   132  			t.Errorf("error reading row %d\nexp=%q\ngot=%q\n",
   133  				irow, exp, got,
   134  			)
   135  		}
   136  		irow++
   137  	}
   138  
   139  	err = rows.Err()
   140  	if err != nil {
   141  		t.Errorf("error iterating over rows: %+v\n", err)
   142  	}
   143  }
   144  
   145  func TestCSVReaderInvalidScan(t *testing.T) {
   146  	fname := "testdata/simple.csv"
   147  	tbl, err := csvutil.Open(fname)
   148  	if err != nil {
   149  		t.Errorf("could not open %s: %+v\n", fname, err)
   150  	}
   151  	defer tbl.Close()
   152  	tbl.Reader.Comma = ';'
   153  	tbl.Reader.Comment = '#'
   154  
   155  	rows, err := tbl.ReadRows(0, -1)
   156  	if err != nil {
   157  		t.Errorf("could read rows [0, -1): %+v\n", err)
   158  	}
   159  	defer rows.Close()
   160  
   161  	if !rows.Next() {
   162  		t.Fatalf("could not get data")
   163  	}
   164  
   165  	err = rows.Scan()
   166  	if err == nil {
   167  		t.Errorf("expected an error")
   168  	}
   169  
   170  	err = rows.Err()
   171  	if err == nil {
   172  		t.Fatalf("expected a sticky error")
   173  	}
   174  }
   175  
   176  func TestCSVReaderScanEOF(t *testing.T) {
   177  	fname := "testdata/simple.csv"
   178  	tbl, err := csvutil.Open(fname)
   179  	if err != nil {
   180  		t.Errorf("could not open %s: %+v\n", fname, err)
   181  	}
   182  	defer tbl.Close()
   183  	tbl.Reader.Comma = ';'
   184  	tbl.Reader.Comment = '#'
   185  
   186  	rows, err := tbl.ReadRows(0, 12)
   187  	if err != nil {
   188  		t.Errorf("could read rows [0, 12): %+v\n", err)
   189  	}
   190  	defer rows.Close()
   191  
   192  	irow := 0
   193  	for rows.Next() {
   194  		data := struct {
   195  			I int
   196  			F float64
   197  			S string
   198  		}{}
   199  		err = rows.Scan(&data)
   200  		if err != nil {
   201  			if irow == 10 {
   202  				break
   203  			}
   204  			t.Errorf("error reading row %d: %+v\n", irow, err)
   205  		}
   206  		exp := fmt.Sprintf("%d;%d;str-%d", irow, irow, irow)
   207  		got := fmt.Sprintf("%v;%v;%v", data.I, data.F, data.S)
   208  		if exp != got {
   209  			t.Errorf("error reading row %d\nexp=%q\ngot=%q\n",
   210  				irow, exp, got,
   211  			)
   212  		}
   213  		irow++
   214  	}
   215  
   216  	if irow != 10 {
   217  		t.Errorf("error. expected irow==10. got=%v\n", irow)
   218  	}
   219  
   220  	err = rows.Err()
   221  	if err != io.EOF {
   222  		t.Errorf("error: expected io.EOF. got=%+v\n", err)
   223  	}
   224  }
   225  
   226  func TestCSVReaderScanUntilEOF(t *testing.T) {
   227  	fname := "testdata/simple.csv"
   228  	tbl, err := csvutil.Open(fname)
   229  	if err != nil {
   230  		t.Errorf("could not open %s: %+v\n", fname, err)
   231  	}
   232  	defer tbl.Close()
   233  	tbl.Reader.Comma = ';'
   234  	tbl.Reader.Comment = '#'
   235  
   236  	rows, err := tbl.ReadRows(0, -1)
   237  	if err != nil {
   238  		t.Errorf("could read rows [0, -1): %+v\n", err)
   239  	}
   240  	defer rows.Close()
   241  
   242  	irow := 0
   243  	for rows.Next() {
   244  		data := struct {
   245  			I int
   246  			F float64
   247  			S string
   248  		}{}
   249  		err = rows.Scan(&data)
   250  		if err != nil {
   251  			if err == io.EOF {
   252  				break
   253  			}
   254  			t.Errorf("error reading row %d: %+v\n", irow, err)
   255  		}
   256  		exp := fmt.Sprintf("%d;%d;str-%d", irow, irow, irow)
   257  		got := fmt.Sprintf("%v;%v;%v", data.I, data.F, data.S)
   258  		if exp != got {
   259  			t.Errorf("error reading row %d\nexp=%q\ngot=%q\n",
   260  				irow, exp, got,
   261  			)
   262  		}
   263  		irow++
   264  	}
   265  
   266  	err = rows.Err()
   267  	if err != io.EOF {
   268  		t.Errorf("error: expected io.EOF. got=%+v\n", err)
   269  	}
   270  }
   271  
   272  func TestCSVReaderScanArgsSubSample(t *testing.T) {
   273  	fname := "testdata/simple.csv"
   274  	tbl, err := csvutil.Open(fname)
   275  	if err != nil {
   276  		t.Errorf("could not open %s: %+v\n", fname, err)
   277  	}
   278  	defer tbl.Close()
   279  	tbl.Reader.Comma = ';'
   280  	tbl.Reader.Comment = '#'
   281  
   282  	rows, err := tbl.ReadRows(2, 10)
   283  	if err != nil {
   284  		t.Errorf("could read rows [2, 10): %+v\n", err)
   285  	}
   286  	defer rows.Close()
   287  
   288  	irow := 2
   289  	for rows.Next() {
   290  		var (
   291  			i int
   292  			f float64
   293  			s string
   294  		)
   295  		err = rows.Scan(&i, &f, &s)
   296  		if err != nil {
   297  			t.Errorf("error reading row %d: %+v\n", irow, err)
   298  		}
   299  		exp := fmt.Sprintf("%d;%d;str-%d", irow, irow, irow)
   300  		got := fmt.Sprintf("%v;%v;%v", i, f, s)
   301  		if exp != got {
   302  			t.Errorf("error reading row %d\nexp=%q\ngot=%q\n",
   303  				irow, exp, got,
   304  			)
   305  		}
   306  		irow++
   307  	}
   308  
   309  	err = rows.Err()
   310  	if err != nil {
   311  		t.Errorf("error iterating over rows: %+v\n", err)
   312  	}
   313  
   314  	if irow-2 != 8 {
   315  		t.Errorf("error: got %d rows. expected 8\n", irow-2)
   316  	}
   317  }
   318  
   319  func TestCSVWriterArgs(t *testing.T) {
   320  	fname := "testdata/out-args.csv"
   321  	tbl, err := csvutil.Create(fname)
   322  	if err != nil {
   323  		t.Errorf("could not create %s: %+v\n", fname, err)
   324  	}
   325  	defer tbl.Close()
   326  	tbl.Writer.Comma = ';'
   327  
   328  	err = tbl.WriteHeader("## a simple set of data: int64;float64;string;slice\n")
   329  	if err != nil {
   330  		t.Errorf("error writing header: %+v\n", err)
   331  	}
   332  
   333  	for i := range 10 {
   334  		var (
   335  			f = float64(i)
   336  			s = fmt.Sprintf("str-%d", i)
   337  			l = []int{1, 2, 3, 4}
   338  		)
   339  		err = tbl.WriteRow(i, f, s, l)
   340  		if err != nil {
   341  			t.Errorf("error writing row %d: %+v\n", i, err)
   342  			break
   343  		}
   344  	}
   345  
   346  	err = tbl.Close()
   347  	if err != nil {
   348  		t.Errorf("error closing table: %+v\n", err)
   349  	}
   350  
   351  	err = diff.Files("testdata/write-results.csv", fname)
   352  	if err != nil {
   353  		t.Errorf("files differ: %+v\n", err)
   354  	}
   355  }
   356  
   357  func TestCSVWriterStruct(t *testing.T) {
   358  	fname := "testdata/out-struct.csv"
   359  	tbl, err := csvutil.Create(fname)
   360  	if err != nil {
   361  		t.Errorf("could not create %s: %+v\n", fname, err)
   362  	}
   363  	defer tbl.Close()
   364  	tbl.Writer.Comma = ';'
   365  
   366  	// test WriteHeader w/o a trailing newline
   367  	err = tbl.WriteHeader("## a simple set of data: int64;float64;string;slice")
   368  	if err != nil {
   369  		t.Errorf("error writing header: %+v\n", err)
   370  	}
   371  
   372  	for i := range 10 {
   373  		data := struct {
   374  			I int
   375  			F float64
   376  			S string
   377  			L []int
   378  		}{
   379  			I: i,
   380  			F: float64(i),
   381  			S: fmt.Sprintf("str-%d", i),
   382  			L: []int{1, 2, 3, 4},
   383  		}
   384  		err = tbl.WriteRow(data)
   385  		if err != nil {
   386  			t.Errorf("error writing row %d: %+v\n", i, err)
   387  			break
   388  		}
   389  	}
   390  
   391  	err = tbl.Close()
   392  	if err != nil {
   393  		t.Errorf("error closing table: %+v\n", err)
   394  	}
   395  
   396  	err = diff.Files("testdata/write-results.csv", fname)
   397  	if err != nil {
   398  		t.Errorf("files differ: %+v\n", err)
   399  	}
   400  }
   401  
   402  func TestCSVWriterArgsSlice(t *testing.T) {
   403  	fname := "testdata/out-args-slice.csv"
   404  	tbl, err := csvutil.Create(fname)
   405  	if err != nil {
   406  		t.Fatalf("could not create %s: %+v", fname, err)
   407  	}
   408  	defer tbl.Close()
   409  	tbl.Writer.Comma = ','
   410  
   411  	err = tbl.WriteHeader("## more complicated slices: [][]int{}, [][]string{}, []string{}, float64\n")
   412  	if err != nil {
   413  		t.Fatalf("error writing header: %+v", err)
   414  	}
   415  
   416  	for i := range 10 {
   417  		var (
   418  			lii = [][]int{{1, 2, 3}, {2, 3, 4}, {7, 8, 15}}
   419  			lss = [][]string{{"foo", "bar", "baz"}, {"abc", "def", "ghi"}, {"qwerty"}}
   420  			ls  = []string{"abc", "def", "ghi"}
   421  			f   = float64(i)
   422  		)
   423  		err = tbl.WriteRow(lii, lss, ls, f)
   424  		if err != nil {
   425  			t.Fatalf("error writing row %d: %+v", i, err)
   426  		}
   427  	}
   428  
   429  	err = tbl.Close()
   430  	if err != nil {
   431  		t.Fatalf("error closing table: %+v", err)
   432  	}
   433  
   434  	err = diff.Files("testdata/write-results-slice.csv", fname)
   435  	if err != nil {
   436  		t.Fatalf("files differ: %+v", err)
   437  	}
   438  }
   439  
   440  func TestCSVAppend(t *testing.T) {
   441  	fname := "testdata/append-test.csv"
   442  	tbl, err := csvutil.Create(fname)
   443  	if err != nil {
   444  		t.Fatal(err)
   445  	}
   446  	defer tbl.Close()
   447  
   448  	tbl.Writer.Comma = ';'
   449  
   450  	// test WriteHeader w/o a trailing newline
   451  	err = tbl.WriteHeader("## a simple set of data: int64;float64;string")
   452  	if err != nil {
   453  		t.Errorf("error writing header: %+v\n", err)
   454  	}
   455  
   456  	for i := range 10 {
   457  		data := struct {
   458  			I int
   459  			F float64
   460  			S string
   461  		}{
   462  			I: i,
   463  			F: float64(i),
   464  			S: fmt.Sprintf("str-%d", i),
   465  		}
   466  		err = tbl.WriteRow(data)
   467  		if err != nil {
   468  			t.Errorf("error writing row %d: %+v\n", i, err)
   469  			break
   470  		}
   471  	}
   472  
   473  	err = tbl.Close()
   474  	if err != nil {
   475  		t.Errorf("error closing table: %+v\n", err)
   476  	}
   477  
   478  	// re-open to append
   479  	tbl, err = csvutil.Append(fname)
   480  	if err != nil {
   481  		t.Fatal(err)
   482  	}
   483  	defer tbl.Close()
   484  
   485  	tbl.Writer.Comma = ';'
   486  	for i := 10; i < 20; i++ {
   487  		data := struct {
   488  			I int
   489  			F float64
   490  			S string
   491  		}{
   492  			I: i,
   493  			F: float64(i),
   494  			S: fmt.Sprintf("str-%d", i),
   495  		}
   496  		err = tbl.WriteRow(data)
   497  		if err != nil {
   498  			t.Errorf("error writing row %d: %+v\n", i, err)
   499  			break
   500  		}
   501  	}
   502  
   503  	err = tbl.Close()
   504  	if err != nil {
   505  		t.Fatal(err)
   506  	}
   507  
   508  	err = diff.Files("testdata/append.csv", fname)
   509  	if err != nil {
   510  		t.Errorf("files differ: %+v\n", err)
   511  	}
   512  }
   513  
   514  func TestCSVReaderTypes(t *testing.T) {
   515  	fname := "testdata/types.csv"
   516  	tbl, err := csvutil.Open(fname)
   517  	if err != nil {
   518  		t.Errorf("could not open %s: %+v\n", fname, err)
   519  	}
   520  	defer tbl.Close()
   521  	tbl.Reader.Comma = ';'
   522  	tbl.Reader.Comment = '#'
   523  
   524  	rows, err := tbl.ReadRows(0, 2)
   525  	if err != nil {
   526  		t.Errorf("could read rows [0, 2): %+v\n", err)
   527  	}
   528  	defer rows.Close()
   529  
   530  	const nfields = 14
   531  	type Data struct {
   532  		Bool   bool
   533  		Int    int
   534  		Int8   int8
   535  		Int16  int16
   536  		Int32  int32
   537  		Int64  int64
   538  		UInt   uint
   539  		UInt8  uint8
   540  		UInt16 uint16
   541  		UInt32 uint32
   542  		UInt64 uint64
   543  		F32    float32
   544  		F64    float64
   545  		Str    string
   546  	}
   547  
   548  	wants := []Data{
   549  		{true, +1, -1, -1, -1, -1, +1, +1, +1, +1, +1, 1.1, 1.1, "str-1"},
   550  		{false, -2, -2, -2, -2, -2, +2, +2, +2, +2, +2, 2.2, 2.2, "str-2"},
   551  	}
   552  	irow := 0
   553  	for rows.Next() {
   554  		want := wants[irow]
   555  		{
   556  			var got Data
   557  			err = rows.Scan(&got.Bool, &got.Int, &got.Int8, &got.Int16, &got.Int32, &got.Int64, &got.UInt, &got.UInt8, &got.UInt16, &got.UInt32, &got.UInt64, &got.F32, &got.F64, &got.Str)
   558  			if err != nil {
   559  				t.Errorf("error reading row %d: %+v\n", irow, err)
   560  			}
   561  			if want != got {
   562  				t.Errorf("error reading row %d\ngot= %#v\nwant=%#v\n",
   563  					irow, got, want,
   564  				)
   565  			}
   566  			if got, want := rows.NumFields(), nfields; got != want {
   567  				t.Errorf("invalid number of fields. got=%d. want=%d", got, want)
   568  
   569  			}
   570  			if got, want := len(rows.Fields()), nfields; got != want {
   571  				t.Errorf("invalid number of fields. got=%d. want=%d", got, want)
   572  
   573  			}
   574  		}
   575  		{
   576  			var got Data
   577  			err = rows.Scan(&got)
   578  			if err != nil {
   579  				t.Errorf("error reading row %d: %+v\n", irow, err)
   580  			}
   581  			if want != got {
   582  				t.Errorf("error reading row %d\ngot= %#v\nwant=%#v\n",
   583  					irow, got, want,
   584  				)
   585  			}
   586  			if got, want := rows.NumFields(), nfields; got != want {
   587  				t.Errorf("invalid number of fields. got=%d. want=%d", got, want)
   588  			}
   589  			if got, want := len(rows.Fields()), nfields; got != want {
   590  				t.Errorf("invalid number of fields. got=%d. want=%d", got, want)
   591  			}
   592  		}
   593  		irow++
   594  	}
   595  
   596  	err = rows.Err()
   597  	if err != nil {
   598  		t.Errorf("error iterating over rows: %+v\n", err)
   599  	}
   600  }
   601  
   602  func TestCSVWriterTypes(t *testing.T) {
   603  	fname := "testdata/out-types.csv"
   604  	tbl, err := csvutil.Create(fname)
   605  	if err != nil {
   606  		t.Errorf("could not create %s: %+v\n", fname, err)
   607  	}
   608  	defer tbl.Close()
   609  	tbl.Writer.Comma = ';'
   610  
   611  	// test WriteHeader w/o a trailing newline
   612  	err = tbl.WriteHeader("## supported types: bool;int;int8;int16;int32;int64;uint;uint8;uint16;uint32;uint64;float32;float64;string")
   613  	if err != nil {
   614  		t.Errorf("error writing header: %+v\n", err)
   615  	}
   616  
   617  	type Data struct {
   618  		Bool   bool
   619  		Int    int
   620  		Int8   int8
   621  		Int16  int16
   622  		Int32  int32
   623  		Int64  int64
   624  		UInt   uint
   625  		UInt8  uint8
   626  		UInt16 uint16
   627  		UInt32 uint32
   628  		UInt64 uint64
   629  		F32    float32
   630  		F64    float64
   631  		Str    string
   632  	}
   633  
   634  	wants := []Data{
   635  		{true, +1, -1, -1, -1, -1, +1, +1, +1, +1, +1, 1.1, 1.1, "str-1"},
   636  		{false, -2, -2, -2, -2, -2, +2, +2, +2, +2, +2, 2.2, 2.2, "str-2"},
   637  	}
   638  	for i := range wants {
   639  		err = tbl.WriteRow(wants[i])
   640  		if err != nil {
   641  			t.Errorf("error writing row %d: %+v\n", i, err)
   642  			break
   643  		}
   644  	}
   645  
   646  	err = tbl.Close()
   647  	if err != nil {
   648  		t.Errorf("error closing table: %+v\n", err)
   649  	}
   650  
   651  	err = diff.Files("testdata/types.csv.ref", fname)
   652  	if err != nil {
   653  		t.Errorf("files differ: %+v\n", err)
   654  	}
   655  }