Commit 43090ac7 authored by Kirill Smelkov's avatar Kirill Smelkov

X tests: Factor-out tree-test-env into tTreeEnv

We will need this not only in test update/rebuild.
parent 6ea5920a
......@@ -49,7 +49,6 @@ import (
"flag"
"fmt"
"io"
"io/ioutil"
"math/rand"
"os"
"os/exec"
......@@ -1391,6 +1390,24 @@ func ΔBTest(xtest interface{}) ΔBTestEntry {
return test
}
// tTreeEnv is tree-based testing environment.
//
// It combines TreeSrv and client side access to ZODB with committed trees. XXX
//
// Create it with tTreeEnv().
type tTreeEnv struct {
*testing.T
work string // working directory
treeSrv *TreeSrv
zstor zodb.IStorage
db *zodb.DB
// all committed trees XXX name
commitv []*tTreeCommit
}
// tTreeCommit represent test commit changing a tree.
type tTreeCommit struct {
tree string // the tree in toplogy-encoding
......@@ -1402,6 +1419,52 @@ type tTreeCommit struct {
blkDataTab map[zodb.Oid]string // full snapshot of all ZBlk data @at
}
// tNewTreeEnv creates new tTreeEnv.
func tNewTreeEnv(t *testing.T) *tTreeEnv {
X := exc.Raiseif
t.Helper()
tt := &tTreeEnv{T: t}
var err error
work := t.TempDir()
tt.treeSrv, err = StartTreeSrv(work + "/1.fs"); X(err)
t.Cleanup(func() {
err := tt.treeSrv.Close(); X(err)
})
tt.zstor, err = zodb.Open(context.Background(), tt.treeSrv.zurl, &zodb.OpenOptions{
ReadOnly: true,
}); X(err)
t.Cleanup(func() {
err := tt.zstor.Close(); X(err)
})
tt.db = zodb.NewDB(tt.zstor, &zodb.DBOptions{
// We need objects to be cached, because otherwise it is too
// slow to run the test for many testcases, especially
// xverifyΔBTail_rebuild.
CacheControl: &tZODBCacheEverything{},
})
t.Cleanup(func() {
err := tt.db.Close(); X(err)
})
head := tt.treeSrv.head
t1 := &tTreeCommit{
tree: "T/B:", // treegen.py creates the tree as initially empty
prev: nil,
at: head,
xkv: XGetTree(tt.db, head, tt.treeSrv.treeRoot),
blkDataTab: xGetBlkDataTab(tt.db, head),
δZ: nil,
δxkv: nil,
}
tt.commitv = []*tTreeCommit{t1}
return tt
}
// tZODBCacheEverything is workaround for ZODB/go not implementing real
// live cache for now: Objects get dropped on PDeactivate if cache
// control does not say we need the object to stay in the cache.
......@@ -1411,10 +1474,85 @@ func (_ *tZODBCacheEverything) PCacheClassify(_ zodb.IPersistent) zodb.PCachePol
return zodb.PCachePinObject | zodb.PCacheKeepState
}
// XGetBlkDataTab loads all ZBlk from db@at.
// Head returns most-recently committed tree.
func (t *tTreeEnv) Head() *tTreeCommit {
return t.commitv[len(t.commitv)-1]
}
// CommitTree calls tg.Commit and returns tTreeCommit corresponding to committed transaction.
func (t *tTreeEnv) CommitTree(tree string) *tTreeCommit {
// TODO X = FatalIf
X := exc.Raiseif
defer exc.Contextf("commit %s", tree)
watchq := make(chan zodb.Event)
at0 := t.zstor.AddWatch(watchq)
defer t.zstor.DelWatch(watchq)
tid, err := t.treeSrv.Commit(tree); X(err)
if !(tid > at0) {
exc.Raisef("treegen -> %s ; want > %s", tid, at0)
}
zevent := <-watchq
δZ := zevent.(*zodb.EventCommit)
if δZ.Tid != tid {
exc.Raisef("treegen -> %s ; watchq -> %s", tid, δZ)
}
// load tree structure from the db
// if the tree does not exist yet - report its structure as empty
var xkv RBucketSet
if tree != DEL {
xkv = XGetTree(t.db, δZ.Tid, t.treeSrv.treeRoot)
} else {
// empty tree with real treeSrv.treeRoot as oid even though the tree is
// deleted. Having real oid in the root tests that after deletion,
// root of the tree stays in the tracking set. We need root to stay
// in trackSet because e.g. in
//
// T1 -> ø -> T2
//
// where the tree is first deleted, then recreated, without root
// staying in trackSet after ->ø, treediff will notice nothing when
// it comes to ->T2.
xkv = RBucketSet{
&RBucket{
oid: zodb.InvalidOid,
parent: &RTree{
oid: t.treeSrv.treeRoot, // NOTE oid is not InvalidOid
parent: nil,
},
lo: KeyMin,
hi_: KeyMax,
kv: map[Key]string{},
},
}
}
ttree := &tTreeCommit{
tree: tree,
at: δZ.Tid,
δZ: δZ,
xkv: xkv,
blkDataTab: xGetBlkDataTab(t.db, δZ.Tid),
}
tprev := t.Head()
ttree.prev = tprev
ttree.δxkv = kvdiff(tprev.xkv.Flatten(), ttree.xkv.Flatten())
t.commitv = append(t.commitv, ttree)
return ttree
}
// xGetBlkDataTab loads all ZBlk from db@at.
//
// it returns {} oid -> blkdata.
func XGetBlkDataTab(db *zodb.DB, at zodb.Tid) map[zodb.Oid]string {
func xGetBlkDataTab(db *zodb.DB, at zodb.Tid) map[zodb.Oid]string {
defer exc.Contextf("%s: @%s: get blkdatatab", db.Storage().URL(), at)
X := exc.Raiseif
......@@ -1474,108 +1612,13 @@ func (t *tTreeCommit) xgetBlkData(oid zodb.Oid) string {
}
// testΔBTail verifies ΔBTail on sequence of tree topologies coming from testq.
func testΔBTail(t *testing.T, testq chan ΔBTestEntry) {
X := exc.Raiseif
work, err := ioutil.TempDir("", "δBTail"); X(err)
defer func() {
err := os.RemoveAll(work); X(err)
}()
tg, err := StartTreeSrv(work + "/1.fs"); X(err)
defer func() {
err := tg.Close(); X(err)
}()
zstor, err := zodb.Open(context.Background(), tg.zurl, &zodb.OpenOptions{
ReadOnly: true,
}); X(err)
defer func() {
err := zstor.Close(); X(err)
}()
db := zodb.NewDB(zstor, &zodb.DBOptions{
// We need objects to be cached, because otherwise it is too
// slow to run the test for many testcases, especially
// xverifyΔBTail_rebuild.
CacheControl: &tZODBCacheEverything{},
})
defer func() {
err := db.Close(); X(err)
}()
// XCommitTree calls tg.Commit and returns tTreeCommit corresponding to committed transaction.
XCommitTree := func(tree string) *tTreeCommit {
defer exc.Contextf("commit %s", tree)
watchq := make(chan zodb.Event)
at0 := zstor.AddWatch(watchq)
defer zstor.DelWatch(watchq)
tid, err := tg.Commit(tree); X(err)
if !(tid > at0) {
exc.Raisef("treegen -> %s ; want > %s", tid, at0)
}
zevent := <-watchq
δZ := zevent.(*zodb.EventCommit)
if δZ.Tid != tid {
exc.Raisef("treegen -> %s ; watchq -> %s", tid, δZ)
}
// load tree structure from the db
// if the tree does not exist yet - report its structure as empty
var xkv RBucketSet
if tree != DEL {
xkv = XGetTree(db, δZ.Tid, tg.treeRoot)
} else {
// empty tree with real tg.treeRoot as oid even though the tree is
// deleted. Having real oid in the root tests that after deletion,
// root of the tree stays in the tracking set. We need root to stay
// in trackSet because e.g. in
//
// T1 -> ø -> T2
//
// where the tree is first deleted, then recreated, without root
// staying in trackSet after ->ø, treediff will notice nothing when
// it comes to ->T2.
xkv = RBucketSet{
&RBucket{
oid: zodb.InvalidOid,
parent: &RTree{
oid: tg.treeRoot, // NOTE oid is not InvalidOid
parent: nil,
},
lo: KeyMin,
hi_: KeyMax,
kv: map[Key]string{},
},
}
}
return &tTreeCommit{
tree: tree,
at: δZ.Tid,
δZ: δZ,
xkv: xkv,
blkDataTab: XGetBlkDataTab(db, δZ.Tid),
}
}
func testΔBTail(t_ *testing.T, testq chan ΔBTestEntry) {
t := tNewTreeEnv(t_)
var t0 *tTreeCommit
t1 := &tTreeCommit{
tree: "T/B:", // treegen.py creates the tree as initially empty
prev: nil,
at: tg.head,
xkv: XGetTree(db, tg.head, tg.treeRoot),
blkDataTab: XGetBlkDataTab(db, tg.head),
δZ: nil,
δxkv: nil,
}
for test := range testq {
t2 := XCommitTree(test.tree)
t2.δxkv = kvdiff(t1.xkv.Flatten(), t2.xkv.Flatten())
t2.prev = t1
t1 := t.Head()
t2 := t.CommitTree(test.tree)
subj := fmt.Sprintf("%s -> %s", t1.tree, t2.tree)
//t.Logf("\n\n\n**** %s ****\n\n", subj)
......@@ -1591,11 +1634,11 @@ func testΔBTail(t *testing.T, testq chan ΔBTestEntry) {
}
// ΔBTail.Update
xverifyΔBTail_Update(t, subj, db, tg.treeRoot, t1,t2)
xverifyΔBTail_Update(t.T, subj, t.db, t.treeSrv.treeRoot, t1,t2)
// ΔBTail.rebuild
if t0 != nil {
xverifyΔBTail_rebuild(t, db, tg.treeRoot, t0,t1,t2)
xverifyΔBTail_rebuild(t.T, t.db, t.treeSrv.treeRoot, t0,t1,t2)
}
t0, t1 = t1, t2
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment