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