github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/pkg/index/sqlite/sqlite_test.go (about)

     1  /*
     2  Copyright 2012 The Camlistore Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8       http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package sqlite_test
    18  
    19  import (
    20  	"bytes"
    21  	"database/sql"
    22  	"fmt"
    23  	"io/ioutil"
    24  	"os"
    25  	"os/exec"
    26  	"sync"
    27  	"testing"
    28  
    29  	"camlistore.org/pkg/index"
    30  	"camlistore.org/pkg/index/indextest"
    31  	"camlistore.org/pkg/sorted"
    32  	"camlistore.org/pkg/sorted/kvtest"
    33  	"camlistore.org/pkg/sorted/sqlite"
    34  
    35  	_ "camlistore.org/third_party/github.com/mattn/go-sqlite3"
    36  )
    37  
    38  var (
    39  	once        sync.Once
    40  	dbAvailable bool
    41  )
    42  
    43  func do(db *sql.DB, sql string) {
    44  	_, err := db.Exec(sql)
    45  	if err == nil {
    46  		return
    47  	}
    48  	panic(fmt.Sprintf("Error %v running SQL: %s", err, sql))
    49  }
    50  
    51  func newSorted(t *testing.T) (kv sorted.KeyValue, clean func()) {
    52  	f, err := ioutil.TempFile("", "sqlite-test")
    53  	if err != nil {
    54  		t.Fatal(err)
    55  	}
    56  	db, err := sql.Open("sqlite3", f.Name())
    57  	if err != nil {
    58  		t.Fatalf("opening test database: %v", err)
    59  	}
    60  	for _, tableSql := range sqlite.SQLCreateTables() {
    61  		do(db, tableSql)
    62  	}
    63  	do(db, fmt.Sprintf(`REPLACE INTO meta VALUES ('version', '%d')`, sqlite.SchemaVersion()))
    64  
    65  	kv, err = sqlite.NewKeyValue(f.Name())
    66  	if err != nil {
    67  		t.Fatal(err)
    68  	}
    69  	return kv, func() {
    70  		kv.Close()
    71  		os.Remove(f.Name())
    72  	}
    73  }
    74  
    75  func TestSortedKV(t *testing.T) {
    76  	kv, clean := newSorted(t)
    77  	defer clean()
    78  	kvtest.TestSorted(t, kv)
    79  }
    80  
    81  type tester struct{}
    82  
    83  func (tester) test(t *testing.T, tfn func(*testing.T, func() *index.Index)) {
    84  	var mu sync.Mutex // guards cleanups
    85  	var cleanups []func()
    86  	defer func() {
    87  		mu.Lock() // never unlocked
    88  		for _, fn := range cleanups {
    89  			fn()
    90  		}
    91  	}()
    92  	makeIndex := func() *index.Index {
    93  		s, cleanup := newSorted(t)
    94  		mu.Lock()
    95  		cleanups = append(cleanups, cleanup)
    96  		mu.Unlock()
    97  		return index.New(s)
    98  	}
    99  	tfn(t, makeIndex)
   100  }
   101  
   102  func TestIndex_SQLite(t *testing.T) {
   103  	tester{}.test(t, indextest.Index)
   104  }
   105  
   106  func TestPathsOfSignerTarget_SQLite(t *testing.T) {
   107  	tester{}.test(t, indextest.PathsOfSignerTarget)
   108  }
   109  
   110  func TestFiles_SQLite(t *testing.T) {
   111  	tester{}.test(t, indextest.Files)
   112  }
   113  
   114  func TestEdgesTo_SQLite(t *testing.T) {
   115  	tester{}.test(t, indextest.EdgesTo)
   116  }
   117  
   118  func TestDelete_SQLite(t *testing.T) {
   119  	tester{}.test(t, indextest.Delete)
   120  }
   121  
   122  func TestConcurrency(t *testing.T) {
   123  	if testing.Short() {
   124  		t.Logf("skipping for short mode")
   125  		return
   126  	}
   127  	s, clean := newSorted(t)
   128  	defer clean()
   129  	const n = 100
   130  	ch := make(chan error)
   131  	for i := 0; i < n; i++ {
   132  		i := i
   133  		go func() {
   134  			bm := s.BeginBatch()
   135  			bm.Set("keyA-"+fmt.Sprint(i), fmt.Sprintf("valA=%d", i))
   136  			bm.Set("keyB-"+fmt.Sprint(i), fmt.Sprintf("valB=%d", i))
   137  			ch <- s.CommitBatch(bm)
   138  		}()
   139  	}
   140  	for i := 0; i < n; i++ {
   141  		if err := <-ch; err != nil {
   142  			t.Errorf("%d: %v", i, err)
   143  		}
   144  	}
   145  }
   146  
   147  func numFDs(t *testing.T) int {
   148  	lsofPath, err := exec.LookPath("lsof")
   149  	if err != nil {
   150  		t.Skipf("No lsof available; skipping test")
   151  	}
   152  	out, err := exec.Command(lsofPath, "-n", "-p", fmt.Sprint(os.Getpid())).Output()
   153  	if err != nil {
   154  		t.Skipf("Error running lsof; skipping test: %s", err)
   155  	}
   156  	return bytes.Count(out, []byte("\n")) - 1 // hacky
   157  }
   158  
   159  func TestFDLeak(t *testing.T) {
   160  	if testing.Short() {
   161  		t.Skip("Skipping in short mode.")
   162  	}
   163  	fd0 := numFDs(t)
   164  	t.Logf("fd0 = %d", fd0)
   165  
   166  	s, clean := newSorted(t)
   167  	defer clean()
   168  
   169  	bm := s.BeginBatch()
   170  	const numRows = 150 // 3x the batchSize of 50 in sqlindex.go; to gaurantee we do multiple batches
   171  	for i := 0; i < numRows; i++ {
   172  		bm.Set(fmt.Sprintf("key:%05d", i), fmt.Sprint(i))
   173  	}
   174  	if err := s.CommitBatch(bm); err != nil {
   175  		t.Fatal(err)
   176  	}
   177  	for i := 0; i < 5; i++ {
   178  		it := s.Find("key:", "key~")
   179  		n := 0
   180  		for it.Next() {
   181  			n++
   182  		}
   183  		if n != numRows {
   184  			t.Errorf("iterated over %d rows; want %d", n, numRows)
   185  		}
   186  		it.Close()
   187  		t.Logf("fd after iteration %d = %d", i, numFDs(t))
   188  	}
   189  }