github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/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/jsonconfig"
    32  	"camlistore.org/pkg/sorted"
    33  	"camlistore.org/pkg/sorted/kvtest"
    34  	"camlistore.org/pkg/sorted/sqlite"
    35  
    36  	_ "camlistore.org/third_party/github.com/mattn/go-sqlite3"
    37  )
    38  
    39  var (
    40  	once        sync.Once
    41  	dbAvailable bool
    42  )
    43  
    44  func do(db *sql.DB, sql string) {
    45  	_, err := db.Exec(sql)
    46  	if err == nil {
    47  		return
    48  	}
    49  	panic(fmt.Sprintf("Error %v running SQL: %s", err, sql))
    50  }
    51  
    52  func newSorted(t *testing.T) (kv sorted.KeyValue, clean func()) {
    53  	f, err := ioutil.TempFile("", "sqlite-test")
    54  	if err != nil {
    55  		t.Fatal(err)
    56  	}
    57  	db, err := sql.Open("sqlite3", f.Name())
    58  	if err != nil {
    59  		t.Fatalf("opening test database: %v", err)
    60  	}
    61  	for _, tableSql := range sqlite.SQLCreateTables() {
    62  		do(db, tableSql)
    63  	}
    64  	do(db, fmt.Sprintf(`REPLACE INTO meta VALUES ('version', '%d')`, sqlite.SchemaVersion()))
    65  
    66  	kv, err = sorted.NewKeyValue(jsonconfig.Obj{
    67  		"type": "sqlite",
    68  		"file": f.Name(),
    69  	})
    70  	if err != nil {
    71  		t.Fatal(err)
    72  	}
    73  	return kv, func() {
    74  		kv.Close()
    75  		os.Remove(f.Name())
    76  	}
    77  }
    78  
    79  func TestSorted_SQLite(t *testing.T) {
    80  	kv, clean := newSorted(t)
    81  	defer clean()
    82  	kvtest.TestSorted(t, kv)
    83  }
    84  
    85  type tester struct{}
    86  
    87  func (tester) test(t *testing.T, tfn func(*testing.T, func() *index.Index)) {
    88  	var mu sync.Mutex // guards cleanups
    89  	var cleanups []func()
    90  	defer func() {
    91  		mu.Lock() // never unlocked
    92  		for _, fn := range cleanups {
    93  			fn()
    94  		}
    95  	}()
    96  	makeIndex := func() *index.Index {
    97  		s, cleanup := newSorted(t)
    98  		mu.Lock()
    99  		cleanups = append(cleanups, cleanup)
   100  		mu.Unlock()
   101  		return index.MustNew(t, s)
   102  	}
   103  	tfn(t, makeIndex)
   104  }
   105  
   106  func TestIndex_SQLite(t *testing.T) {
   107  	tester{}.test(t, indextest.Index)
   108  }
   109  
   110  func TestPathsOfSignerTarget_SQLite(t *testing.T) {
   111  	tester{}.test(t, indextest.PathsOfSignerTarget)
   112  }
   113  
   114  func TestFiles_SQLite(t *testing.T) {
   115  	tester{}.test(t, indextest.Files)
   116  }
   117  
   118  func TestEdgesTo_SQLite(t *testing.T) {
   119  	tester{}.test(t, indextest.EdgesTo)
   120  }
   121  
   122  func TestDelete_SQLite(t *testing.T) {
   123  	tester{}.test(t, indextest.Delete)
   124  }
   125  
   126  func TestConcurrency(t *testing.T) {
   127  	if testing.Short() {
   128  		t.Logf("skipping for short mode")
   129  		return
   130  	}
   131  	s, clean := newSorted(t)
   132  	defer clean()
   133  	const n = 100
   134  	ch := make(chan error)
   135  	for i := 0; i < n; i++ {
   136  		i := i
   137  		go func() {
   138  			bm := s.BeginBatch()
   139  			bm.Set("keyA-"+fmt.Sprint(i), fmt.Sprintf("valA=%d", i))
   140  			bm.Set("keyB-"+fmt.Sprint(i), fmt.Sprintf("valB=%d", i))
   141  			ch <- s.CommitBatch(bm)
   142  		}()
   143  	}
   144  	for i := 0; i < n; i++ {
   145  		if err := <-ch; err != nil {
   146  			t.Errorf("%d: %v", i, err)
   147  		}
   148  	}
   149  }
   150  
   151  func numFDs(t *testing.T) int {
   152  	lsofPath, err := exec.LookPath("lsof")
   153  	if err != nil {
   154  		t.Skipf("No lsof available; skipping test")
   155  	}
   156  	out, err := exec.Command(lsofPath, "-n", "-p", fmt.Sprint(os.Getpid())).Output()
   157  	if err != nil {
   158  		t.Skipf("Error running lsof; skipping test: %s", err)
   159  	}
   160  	return bytes.Count(out, []byte("\n")) - 1 // hacky
   161  }
   162  
   163  func TestFDLeak(t *testing.T) {
   164  	if testing.Short() {
   165  		t.Skip("Skipping in short mode.")
   166  	}
   167  	fd0 := numFDs(t)
   168  	t.Logf("fd0 = %d", fd0)
   169  
   170  	s, clean := newSorted(t)
   171  	defer clean()
   172  
   173  	bm := s.BeginBatch()
   174  	const numRows = 150 // 3x the batchSize of 50 in sqlindex.go; to gaurantee we do multiple batches
   175  	for i := 0; i < numRows; i++ {
   176  		bm.Set(fmt.Sprintf("key:%05d", i), fmt.Sprint(i))
   177  	}
   178  	if err := s.CommitBatch(bm); err != nil {
   179  		t.Fatal(err)
   180  	}
   181  	for i := 0; i < 5; i++ {
   182  		it := s.Find("key:", "key~")
   183  		n := 0
   184  		for it.Next() {
   185  			n++
   186  		}
   187  		if n != numRows {
   188  			t.Errorf("iterated over %d rows; want %d", n, numRows)
   189  		}
   190  		it.Close()
   191  		t.Logf("fd after iteration %d = %d", i, numFDs(t))
   192  	}
   193  }