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 }