testutil.go (7880B)
1 package dbtest 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/hex" 7 "errors" 8 "fmt" 9 "path" 10 "testing" 11 12 "git.defalsify.org/vise.git/db" 13 "git.defalsify.org/vise.git/lang" 14 ) 15 16 type testCase struct { 17 typ uint8 18 s string 19 k []byte 20 v []byte 21 x []byte 22 l *lang.Language 23 t string 24 } 25 26 type testVector struct { 27 c map[string]*testCase 28 v []string 29 i int 30 s string 31 } 32 33 type testFunc func() testVector 34 35 var ( 36 tests = []testFunc{ 37 generateSessionTestVectors, 38 generateMultiSessionTestVectors, 39 generateLanguageTestVectors, 40 generateMultiLanguageTestVectors, 41 generateSessionLanguageTestVectors, 42 } 43 dataTypeDebug = map[uint8]string{ 44 db.DATATYPE_BIN: "bytecode", 45 db.DATATYPE_TEMPLATE: "template", 46 db.DATATYPE_MENU: "menu", 47 db.DATATYPE_STATICLOAD: "staticload", 48 db.DATATYPE_STATE: "state", 49 db.DATATYPE_USERDATA: "udata", 50 } 51 ) 52 53 func (tc *testCase) Key() []byte { 54 return tc.k 55 } 56 57 func (tc *testCase) Val() []byte { 58 return tc.v 59 } 60 61 func (tc *testCase) Typ() uint8 { 62 return tc.typ 63 } 64 65 func (tc *testCase) Session() string { 66 return tc.s 67 } 68 69 func (tc *testCase) Lang() string { 70 if tc.l == nil { 71 return "" 72 } 73 return tc.l.Code 74 } 75 76 func (tc *testCase) Expect() []byte { 77 return tc.x 78 } 79 80 func (tc *testCase) Label() string { 81 return tc.t 82 } 83 84 func (tv *testVector) add(typ uint8, k string, v string, session string, expect string, language string) { 85 var b []byte 86 var x []byte 87 var err error 88 var ln *lang.Language 89 90 if typ == db.DATATYPE_BIN { 91 b, err = hex.DecodeString(v) 92 if err != nil { 93 panic(err) 94 } 95 x, err = hex.DecodeString(expect) 96 if err != nil { 97 panic(err) 98 } 99 } else { 100 b = []byte(v) 101 x = []byte(expect) 102 } 103 104 if language != "" { 105 lo, err := lang.LanguageFromCode(language) 106 if err != nil { 107 panic(err) 108 } 109 ln = &lo 110 } 111 s := dataTypeDebug[typ] 112 s = path.Join(s, session) 113 s = path.Join(s, k) 114 if ln != nil { 115 s = path.Join(s, language) 116 } 117 o := &testCase{ 118 typ: typ, 119 k: []byte(k), 120 v: b, 121 s: session, 122 x: x, 123 l: ln, 124 t: s, 125 } 126 tv.c[s] = o 127 i := len(tv.v) 128 tv.v = append(tv.v, s) 129 logg.Tracef("add testcase", "i", i, "s", s, "k", o.k) 130 } 131 132 func (tv *testVector) next() (int, *testCase) { 133 i := tv.i 134 if i == len(tv.v) { 135 return -1, nil 136 } 137 tv.i++ 138 return i, tv.c[tv.v[i]] 139 } 140 141 func (tv *testVector) rewind() { 142 tv.i = 0 143 } 144 145 func (tv *testVector) put(ctx context.Context, db db.Db) error { 146 var i int 147 var tc *testCase 148 defer tv.rewind() 149 150 for true { 151 i, tc = tv.next() 152 if i == -1 { 153 break 154 } 155 logg.TraceCtxf(ctx, "running put for test", "vector", tv.label(), "case", tc.Label()) 156 db.SetPrefix(tc.Typ()) 157 db.SetSession(tc.Session()) 158 db.SetLock(tc.Typ(), false) 159 db.SetLanguage(nil) 160 if tc.Lang() != "" { 161 ln, err := lang.LanguageFromCode(tc.Lang()) 162 if err != nil { 163 return err 164 } 165 db.SetLanguage(&ln) 166 } 167 err := db.Put(ctx, tc.Key(), tc.Val()) 168 if err != nil { 169 return err 170 } 171 db.SetLock(tc.Typ(), true) 172 } 173 return nil 174 } 175 176 func (tv *testVector) label() string { 177 return tv.s 178 } 179 180 func generateSessionTestVectors() testVector { 181 tv := testVector{c: make(map[string]*testCase), s: "session"} 182 tv.add(db.DATATYPE_BIN, "foo", "deadbeef", "", "beeffeed", "") 183 tv.add(db.DATATYPE_BIN, "foo", "beeffeed", "inky", "beeffeed", "") 184 tv.add(db.DATATYPE_TEMPLATE, "foo", "tinkywinky", "", "dipsy", "") 185 tv.add(db.DATATYPE_TEMPLATE, "foo", "dipsy", "pinky", "dipsy", "") 186 tv.add(db.DATATYPE_MENU, "foo", "lala", "", "pu", "") 187 tv.add(db.DATATYPE_MENU, "foo", "pu", "blinky", "pu", "") 188 tv.add(db.DATATYPE_STATICLOAD, "foo", "bar", "", "baz", "") 189 tv.add(db.DATATYPE_STATICLOAD, "foo", "baz", "clyde", "baz", "") 190 tv.add(db.DATATYPE_STATE, "foo", "xyzzy", "", "xyzzy", "") 191 tv.add(db.DATATYPE_STATE, "foo", "plugh", "sue", "plugh", "") 192 tv.add(db.DATATYPE_USERDATA, "foo", "itchy", "", "itchy", "") 193 tv.add(db.DATATYPE_USERDATA, "foo", "scratchy", "poochie", "scratchy", "") 194 return tv 195 } 196 197 func generateLanguageTestVectors() testVector { 198 tv := testVector{c: make(map[string]*testCase), s: "language"} 199 tv.add(db.DATATYPE_BIN, "foo", "deadbeef", "", "beeffeed", "") 200 tv.add(db.DATATYPE_BIN, "foo", "beeffeed", "", "beeffeed", "nor") 201 tv.add(db.DATATYPE_TEMPLATE, "foo", "tinkywinky", "", "tinkywinky", "") 202 tv.add(db.DATATYPE_TEMPLATE, "foo", "dipsy", "", "dipsy", "nor") 203 tv.add(db.DATATYPE_MENU, "foo", "lala", "", "lala", "") 204 tv.add(db.DATATYPE_MENU, "foo", "pu", "", "pu", "nor") 205 tv.add(db.DATATYPE_STATICLOAD, "foo", "bar", "", "bar", "") 206 tv.add(db.DATATYPE_STATICLOAD, "foo", "baz", "", "baz", "nor") 207 tv.add(db.DATATYPE_STATE, "foo", "xyzzy", "", "plugh", "") 208 tv.add(db.DATATYPE_STATE, "foo", "plugh", "", "plugh", "nor") 209 tv.add(db.DATATYPE_USERDATA, "foo", "itchy", "", "scratchy", "") 210 tv.add(db.DATATYPE_USERDATA, "foo", "scratchy", "", "scratchy", "nor") 211 return tv 212 } 213 214 func generateMultiLanguageTestVectors() testVector { 215 tv := testVector{c: make(map[string]*testCase), s: "multilanguage"} 216 tv.add(db.DATATYPE_TEMPLATE, "foo", "tinkywinky", "", "pu", "") 217 tv.add(db.DATATYPE_TEMPLATE, "foo", "dipsy", "", "dipsy", "nor") 218 tv.add(db.DATATYPE_TEMPLATE, "foo", "lala", "", "lala", "swa") 219 tv.add(db.DATATYPE_TEMPLATE, "foo", "pu", "", "pu", "") 220 return tv 221 } 222 223 func generateSessionLanguageTestVectors() testVector { 224 tv := testVector{c: make(map[string]*testCase), s: "sessionlanguage"} 225 tv.add(db.DATATYPE_TEMPLATE, "foo", "tinkywinky", "", "pu", "") 226 tv.add(db.DATATYPE_TEMPLATE, "foo", "dipsy", "", "lala", "nor") 227 tv.add(db.DATATYPE_TEMPLATE, "foo", "lala", "bar", "lala", "nor") 228 tv.add(db.DATATYPE_TEMPLATE, "foo", "pu", "bar", "pu", "") 229 tv.add(db.DATATYPE_STATE, "foo", "inky", "", "pinky", "") 230 tv.add(db.DATATYPE_STATE, "foo", "pinky", "", "pinky", "nor") 231 tv.add(db.DATATYPE_STATE, "foo", "blinky", "bar", "clyde", "nor") 232 tv.add(db.DATATYPE_STATE, "foo", "clyde", "bar", "clyde", "") 233 tv.add(db.DATATYPE_BIN, "foo", "deadbeef", "", "feebdaed", "") 234 tv.add(db.DATATYPE_BIN, "foo", "beeffeed", "", "feebdaed", "nor") 235 tv.add(db.DATATYPE_BIN, "foo", "deeffeeb", "baz", "feebdaed", "nor") 236 tv.add(db.DATATYPE_BIN, "foo", "feebdaed", "baz", "feebdaed", "") 237 return tv 238 } 239 240 func generateMultiSessionTestVectors() testVector { 241 tv := testVector{c: make(map[string]*testCase), s: "multisession"} 242 tv.add(db.DATATYPE_TEMPLATE, "foo", "red", "", "blue", "") 243 tv.add(db.DATATYPE_TEMPLATE, "foo", "green", "bar", "blue", "") 244 tv.add(db.DATATYPE_TEMPLATE, "foo", "blue", "baz", "blue", "") 245 tv.add(db.DATATYPE_STATE, "foo", "inky", "", "inky", "") 246 tv.add(db.DATATYPE_STATE, "foo", "pinky", "clyde", "pinky", "") 247 tv.add(db.DATATYPE_STATE, "foo", "blinky", "sue", "blinky", "") 248 tv.add(db.DATATYPE_BIN, "foo", "deadbeef", "", "feebdeef", "") 249 tv.add(db.DATATYPE_BIN, "foo", "feedbeef", "bar", "feebdeef", "") 250 tv.add(db.DATATYPE_BIN, "foo", "feebdeef", "baz", "feebdeef", "") 251 return tv 252 } 253 254 func runTest(t *testing.T, ctx context.Context, db db.Db, vs testVector) error { 255 err := vs.put(ctx, db) 256 if err != nil { 257 return err 258 } 259 for true { 260 i, tc := vs.next() 261 if i == -1 { 262 break 263 } 264 s := fmt.Sprintf("Test%s[%d]%s", vs.label(), i, tc.Label()) 265 r := t.Run(s, func(t *testing.T) { 266 db.SetPrefix(tc.Typ()) 267 db.SetSession(tc.Session()) 268 if tc.Lang() != "" { 269 ln, err := lang.LanguageFromCode(tc.Lang()) 270 if err != nil { 271 t.Fatal(err) 272 } 273 db.SetLanguage(&ln) 274 } else { 275 db.SetLanguage(nil) 276 } 277 db.SetSession(tc.Session()) 278 v, err := db.Get(ctx, tc.Key()) 279 if err != nil { 280 t.Fatal(err) 281 } 282 if !bytes.Equal(tc.Expect(), v) { 283 t.Fatalf("expected %s, got %s", tc.Expect(), v) 284 } 285 }) 286 if !r { 287 return errors.New("subtest fail") 288 } 289 } 290 return nil 291 292 } 293 294 func runTests(t *testing.T, ctx context.Context, db db.Db) error { 295 for _, fn := range tests { 296 err := runTest(t, ctx, db, fn()) 297 if err != nil { 298 return err 299 } 300 } 301 302 return nil 303 } 304 305 func RunTests(t *testing.T, ctx context.Context, db db.Db) error { 306 return runTests(t, ctx, db) 307 }