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 }