go-hep.org/x/hep@v0.38.1/csvutil/csvdriver/driver_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 csvdriver_test
     6  
     7  import (
     8  	"database/sql"
     9  	"fmt"
    10  	"os"
    11  	"reflect"
    12  	"testing"
    13  	"time"
    14  
    15  	"go-hep.org/x/hep/csvutil/csvdriver"
    16  )
    17  
    18  func testDB(t *testing.T, conn csvdriver.Conn, vars string) {
    19  	db, err := conn.Open()
    20  	if err != nil {
    21  		t.Errorf("%s: error opening CSV file", conn.File)
    22  		return
    23  	}
    24  	defer db.Close()
    25  
    26  	tx, err := db.Begin()
    27  	if err != nil {
    28  		t.Errorf("%s: error starting tx: %+v", conn.File, err)
    29  		return
    30  	}
    31  	defer func() {
    32  		_ = tx.Commit()
    33  	}()
    34  
    35  	done := make(chan error)
    36  	go func() {
    37  		done <- db.Ping()
    38  	}()
    39  
    40  	select {
    41  	case <-time.After(2 * time.Second):
    42  		t.Errorf("%s: ping timeout", conn.File)
    43  		return
    44  	case err := <-done:
    45  		if err != nil {
    46  			t.Errorf("%s: error pinging db: %+v\n", conn.File, err)
    47  			return
    48  		}
    49  	}
    50  
    51  	rows, err := tx.Query("select " + vars + " from csv order by id();")
    52  	if err != nil {
    53  		t.Errorf("%s: error querying db: %+v\n", conn.File, err)
    54  		return
    55  	}
    56  	defer rows.Close()
    57  
    58  	type dataType struct {
    59  		i int64
    60  		f float64
    61  		s string
    62  	}
    63  
    64  	var got []dataType
    65  	for rows.Next() {
    66  		var data dataType
    67  		err = rows.Scan(&data.i, &data.f, &data.s)
    68  		if err != nil {
    69  			t.Errorf("%s: error scanning db: %+v\n", conn.File, err)
    70  			return
    71  		}
    72  		got = append(got, data)
    73  	}
    74  
    75  	err = rows.Close()
    76  	if err != nil {
    77  		t.Errorf("%s: error closing rows: %+v\n", conn.File, err)
    78  		return
    79  	}
    80  
    81  	err = db.Close()
    82  	if err != nil {
    83  		t.Errorf("%s: error closing db: %+v\n", conn.File, err)
    84  		return
    85  	}
    86  
    87  	want := []dataType{
    88  		{0, 0, "str-0"},
    89  		{1, 1, "str-1"},
    90  		{2, 2, "str-2"},
    91  		{3, 3, "str-3"},
    92  		{4, 4, "str-4"},
    93  		{5, 5, "str-5"},
    94  		{6, 6, "str-6"},
    95  		{7, 7, "str-7"},
    96  		{8, 8, "str-8"},
    97  		{9, 9, "str-9"},
    98  	}
    99  
   100  	if !reflect.DeepEqual(got, want) {
   101  		t.Errorf("%s: got=\n%v\nwant=\n%v\n", conn.File, got, want)
   102  		return
   103  	}
   104  }
   105  
   106  func TestOpen(t *testing.T) {
   107  	for _, test := range []struct {
   108  		c csvdriver.Conn
   109  		q string
   110  	}{
   111  		{
   112  			c: csvdriver.Conn{
   113  				File:    "testdata/simple.csv",
   114  				Comment: '#', Comma: ';',
   115  			},
   116  			q: "var1, var2, var3",
   117  		},
   118  		{
   119  			c: csvdriver.Conn{
   120  				File:    "testdata/simple.csv",
   121  				Comment: '#', Comma: ';',
   122  				Names: []string{"v1", "v2", "v3"},
   123  			},
   124  			q: "v1, v2, v3",
   125  		},
   126  		{
   127  			c: csvdriver.Conn{
   128  				File:    "testdata/simple-with-comment.csv",
   129  				Comment: '#', Comma: ';',
   130  			},
   131  			q: "var1, var2, var3",
   132  		},
   133  		{
   134  			c: csvdriver.Conn{
   135  				File:    "testdata/simple-with-comment.csv",
   136  				Comment: '#', Comma: ';',
   137  				Names: []string{"v1", "v2", "v3"},
   138  			},
   139  			q: "v1, v2, v3",
   140  		},
   141  		{
   142  			c: csvdriver.Conn{
   143  				File:    "testdata/simple-with-header.csv",
   144  				Comment: '#', Comma: ';',
   145  				Header: true,
   146  			},
   147  			q: "i64, f64, str",
   148  		},
   149  		{
   150  			c: csvdriver.Conn{
   151  				File: "testdata/simple-with-header.csv", Comment: '#', Comma: ';',
   152  				Header: true,
   153  				Names:  []string{"var1", "var2", "var3"},
   154  			},
   155  			q: "var1, var2, var3",
   156  		},
   157  	} {
   158  		testDB(t, test.c, test.q)
   159  	}
   160  }
   161  
   162  func TestQL(t *testing.T) {
   163  	db, err := sql.Open("ql", "memory://out-create-ql.csv")
   164  	if err != nil {
   165  		t.Fatalf("error creating CSV-QL file: %+v\n", err)
   166  	}
   167  	defer db.Close()
   168  
   169  	err = db.Ping()
   170  	if err != nil {
   171  		t.Fatalf("error pinging db: %+v\n", err)
   172  	}
   173  
   174  	tx, err := db.Begin()
   175  	if err != nil {
   176  		t.Fatalf("error starting transaction: %+v\n", err)
   177  	}
   178  	defer func() {
   179  		_ = tx.Commit()
   180  	}()
   181  
   182  	_, err = tx.Exec("create table csv (var1 int64, var2 float64, var3 string);")
   183  	if err != nil {
   184  		t.Fatalf("error creating table: %+v\n", err)
   185  	}
   186  
   187  	for i := range 10 {
   188  		f := float64(i)
   189  		s := fmt.Sprintf("str-%d", i)
   190  		_, err = tx.Exec("insert into csv values($1,$2,$3);", i, f, s)
   191  		if err != nil {
   192  			t.Fatalf("error inserting row %d: %+v\n", i+1, err)
   193  		}
   194  	}
   195  	err = tx.Commit()
   196  	if err != nil {
   197  		t.Fatalf("error committing transaction: %+v\n", err)
   198  	}
   199  }
   200  
   201  func TestCreate(t *testing.T) {
   202  	const fname = "testdata/out-create.csv"
   203  	defer os.Remove(fname)
   204  
   205  	db, err := csvdriver.Create(fname)
   206  	if err != nil {
   207  		t.Fatalf("error creating CSV file: %+v\n", err)
   208  	}
   209  	defer db.Close()
   210  
   211  	err = db.Ping()
   212  	if err != nil {
   213  		t.Fatalf("error pinging db: %+v\n", err)
   214  	}
   215  
   216  	tx, err := db.Begin()
   217  	if err != nil {
   218  		t.Fatalf("error starting transaction: %+v\n", err)
   219  	}
   220  	defer func() {
   221  		_ = tx.Commit()
   222  	}()
   223  
   224  	_, err = tx.Exec("create table csv (var1 int64, var2 float64, var3 string);")
   225  	if err != nil {
   226  		t.Fatalf("error creating table: %+v\n", err)
   227  	}
   228  
   229  	for i := range 10 {
   230  		f := float64(i)
   231  		s := fmt.Sprintf("str-%d", i)
   232  		_, err = tx.Exec("insert into csv values($1,$2,$3);", i, f, s)
   233  		if err != nil {
   234  			t.Fatalf("error inserting row %d: %+v\n", i+1, err)
   235  		}
   236  	}
   237  	err = tx.Commit()
   238  	if err != nil {
   239  		t.Fatalf("error committing transaction: %+v\n", err)
   240  	}
   241  }
   242  
   243  func TestCreateRollback(t *testing.T) {
   244  	const fname = "testdata/out-create-rollback.csv"
   245  	defer os.Remove(fname)
   246  
   247  	db, err := csvdriver.Create(fname)
   248  	if err != nil {
   249  		t.Fatalf("error creating CSV file: %+v\n", err)
   250  	}
   251  	defer db.Close()
   252  
   253  	err = db.Ping()
   254  	if err != nil {
   255  		t.Fatalf("error pinging db: %+v\n", err)
   256  	}
   257  
   258  	tx, err := db.Begin()
   259  	if err != nil {
   260  		t.Fatalf("error starting transaction: %+v\n", err)
   261  	}
   262  	defer func() {
   263  		_ = tx.Commit()
   264  	}()
   265  
   266  	_, err = tx.Exec("create table csv (var1 int64, var2 float64, var3 string);")
   267  	if err != nil {
   268  		t.Fatalf("error creating table: %+v\n", err)
   269  	}
   270  
   271  	for i := range 10 {
   272  		f := float64(i)
   273  		s := fmt.Sprintf("str-%d", i)
   274  		_, err = tx.Exec("insert into csv values($1,$2,$3);", i, f, s)
   275  		if err != nil {
   276  			t.Fatalf("error inserting row %d: %+v\n", i+1, err)
   277  		}
   278  	}
   279  
   280  	err = tx.Rollback()
   281  	if err != nil {
   282  		t.Fatalf("error committing transaction: %+v\n", err)
   283  	}
   284  
   285  	err = db.Close()
   286  	if err != nil {
   287  		t.Fatal(err)
   288  	}
   289  
   290  	got, err := os.ReadFile(fname)
   291  	if err != nil {
   292  		t.Fatal(err)
   293  	}
   294  
   295  	if len(got) != 0 {
   296  		t.Fatalf("non-empty file:\n%q\n", string(got))
   297  	}
   298  }
   299  
   300  func TestOpenDriver(t *testing.T) {
   301  	for _, fname := range []string{
   302  		"https://codeberg.org/go-hep/hep/raw/branch/main/csvutil/csvdriver/testdata/types.csv",
   303  		"testdata/types.csv",
   304  	} {
   305  		t.Run(fname, func(t *testing.T) {
   306  
   307  			c, err := csvdriver.Open(fname)
   308  			if err != nil {
   309  				t.Fatal(err)
   310  			}
   311  
   312  			stmt, err := c.Prepare("select var1 from csv;")
   313  			if err != nil {
   314  				t.Fatal(err)
   315  			}
   316  			defer stmt.Close()
   317  
   318  			rows, err := stmt.Query()
   319  			if err != nil {
   320  				t.Fatal(err)
   321  			}
   322  			defer rows.Close()
   323  
   324  			v, err := rows.Columns()
   325  			if err != nil {
   326  				t.Fatal(err)
   327  			}
   328  			if got, want := v, []string{"var1"}; !reflect.DeepEqual(got, want) {
   329  				t.Fatalf("got=%v, want=%v", got, want)
   330  			}
   331  
   332  			types, err := rows.ColumnTypes()
   333  			if err != nil {
   334  				t.Fatal(err)
   335  			}
   336  			if got, want := types[0].Name(), "var1"; got != want {
   337  				t.Fatalf("got=%q, want=%q", got, want)
   338  			}
   339  
   340  			err = rows.Close()
   341  			if err != nil {
   342  				t.Fatalf("error closing rows: %+v\n", err)
   343  			}
   344  		})
   345  	}
   346  }