Commit 1b190e2b authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent 4eb451a4
...@@ -17,7 +17,20 @@ ...@@ -17,7 +17,20 @@
// See COPYING file for full licensing terms. // See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options. // See https://www.nexedi.com/licensing for rationale and options.
package zodb package zodb_test
func TestDB() { import (
"lab.nexedi.com/kirr/neo/go/zodb"
"lab.nexedi.com/kirr/neo/go/internal/xtesting"
// wks or any other storage cannot be imported from zodb due to cycle
_ "lab.nexedi.com/kirr/neo/go/zodb/wks"
)
// import at runtime few things into zodb, that zodb cannot import itself due to cyclic dependency.
func init() {
//zodb.ZPyCommit = xtesting.ZPyCommit
zodb.ZPyCommit = func(zurl string, at zodb.Tid, objv ...interface{}) (zodb.Tid, error) {
return xtesting.ZPyCommit(zurl, at) // XXX + objv
}
} }
...@@ -286,6 +286,8 @@ var typeTab = make(map[reflect.Type]*zclass) // {} type -> zclass ...@@ -286,6 +286,8 @@ var typeTab = make(map[reflect.Type]*zclass) // {} type -> zclass
// - if obj's type was registered (RegisterClass) -- corresponding class. // - if obj's type was registered (RegisterClass) -- corresponding class.
// - for Broken objects -- ZODB.Broken("<broken-class>"). // - for Broken objects -- ZODB.Broken("<broken-class>").
// - else -- ZODB.Go("<fully-qualified-type(obj)>") // - else -- ZODB.Go("<fully-qualified-type(obj)>")
//
// XXX -> IPersistent.ZClass() ?
func ClassOf(obj IPersistent) string { func ClassOf(obj IPersistent) string {
zb, broken := obj.(*Broken) zb, broken := obj.(*Broken)
if broken { if broken {
......
...@@ -20,10 +20,16 @@ ...@@ -20,10 +20,16 @@
package zodb package zodb
import ( import (
"context"
"fmt" "fmt"
"io/ioutil"
"os"
"reflect" "reflect"
"testing" "testing"
"lab.nexedi.com/kirr/neo/go/transaction"
"lab.nexedi.com/kirr/go123/exc"
"lab.nexedi.com/kirr/go123/mem" "lab.nexedi.com/kirr/go123/mem"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
...@@ -62,48 +68,58 @@ func init() { ...@@ -62,48 +68,58 @@ func init() {
RegisterClassAlias("t.zodb.MyOldObject", "t.zodb.MyObject") RegisterClassAlias("t.zodb.MyOldObject", "t.zodb.MyObject")
} }
func TestPersistent(t *testing.T) { // _checkObj verifies current state of persistent object.
assert := require.New(t) //
// one can bind _checkObj to t via tCheckObj.
func _checkObj(t testing.TB, obj IPersistent, jar *Connection, oid Oid, serial Tid, state ObjectState, refcnt int32, loading *loadState) {
t.Helper()
xbase := reflect.ValueOf(obj).Elem().FieldByName("Persistent")
pbase := xbase.Addr().Interface().(*Persistent)
// checkObj verifies current state of persistent object. badf := func(format string, argv ...interface{}) {
checkObj := func(obj IPersistent, jar *Connection, oid Oid, serial Tid, state ObjectState, refcnt int32, loading *loadState) {
t.Helper() t.Helper()
xbase := reflect.ValueOf(obj).Elem().FieldByName("Persistent") msg := fmt.Sprintf(format, argv...)
pbase := xbase.Addr().Interface().(*Persistent) t.Fatalf("%#v: %s", obj, msg)
}
badf := func(format string, argv ...interface{}) { zc := pbase.zclass
t.Helper() //zc.class
msg := fmt.Sprintf(format, argv...) if typ := reflect.TypeOf(obj).Elem(); typ != zc.typ {
t.Fatalf("%#v: %s", obj, msg) badf("invalid zclass: .typ = %s ; want %s", zc.typ, typ)
} }
//zc.stateType
zc := pbase.zclass if pbase.jar != jar {
//zc.class badf("invalid jar")
if typ := reflect.TypeOf(obj).Elem(); typ != zc.typ { }
badf("invalid zclass: .typ = %s ; want %s", zc.typ, typ) if pbase.oid != oid {
} badf("invalid oid: %s ; want %s", pbase.oid, oid)
//zc.stateType }
if pbase.serial != serial {
badf("invalid serial: %s ; want %s", pbase.serial, serial)
}
if pbase.state != state {
badf("invalid state: %s ; want %s", pbase.state, state)
}
if pbase.refcnt != refcnt {
badf("invalid refcnt: %s ; want %s", pbase.refcnt, refcnt)
}
if pbase.instance != obj {
badf("base.instance != obj")
}
// XXX loading too?
}
if pbase.jar != jar { func tCheckObj(t testing.TB) func(IPersistent, *Connection, Oid, Tid, ObjectState, int32, *loadState) {
badf("invalid jar") return func(obj IPersistent, jar *Connection, oid Oid, serial Tid, state ObjectState, refcnt int32, loading *loadState) {
} _checkObj(t, obj, jar, oid, serial, state, refcnt, loading)
if pbase.oid != oid {
badf("invalid oid: %s ; want %s", pbase.oid, oid)
}
if pbase.serial != serial {
badf("invalid serial: %s ; want %s", pbase.serial, serial)
}
if pbase.state != state {
badf("invalid state: %s ; want %s", pbase.state, state)
}
if pbase.refcnt != refcnt {
badf("invalid refcnt: %s ; want %s", pbase.refcnt, refcnt)
}
if pbase.instance != obj {
badf("base.instance != obj")
}
// XXX loading too?
} }
}
// basic Persistent tests without storage.
func TestPersistentBasic(t *testing.T) {
assert := require.New(t)
checkObj := tCheckObj(t)
// unknown type -> Broken // unknown type -> Broken
xobj := newGhost("t.unknown", 10, nil) xobj := newGhost("t.unknown", 10, nil)
...@@ -140,11 +156,102 @@ func TestPersistent(t *testing.T) { ...@@ -140,11 +156,102 @@ func TestPersistent(t *testing.T) {
// ClassOf(unregistered-obj) // ClassOf(unregistered-obj)
obj2 := &Unregistered{} obj2 := &Unregistered{}
assert.Equal(ClassOf(obj2), `ZODB.Go("lab.nexedi.com/kirr/neo/go/zodb.Unregistered")`) assert.Equal(ClassOf(obj2), `ZODB.Go("lab.nexedi.com/kirr/neo/go/zodb.Unregistered")`)
}
// zcacheControl is simple live cache control that prevents specified objects
// to be evicted from live cache.
type zcacheControl struct {
keep []Oid // objects that must not be evicted
}
func (cc *zcacheControl) WantEvict(obj IPersistent) bool {
for _, oid := range cc.keep {
if obj.POid() == oid {
return false
}
}
return true
}
var ZPyCommit func(string, Tid, ...interface{}) (Tid, error) // XXX ZObject
// Persistent tests with storage.
func TestPersistentDB(t *testing.T) {
X := exc.Raiseif
assert := require.New(t)
checkObj := tCheckObj(t)
work, err := ioutil.TempDir("", "t-persistent"); X(err)
defer func() {
err := os.RemoveAll(work); X(err)
}()
zurl := work + "/1.fs"
const oid1 = 1 // XXX
const oid2 = 2
// XXX create test db via py (change both oid1 & oid2)
// XXX commit1 to test db
at1, err := ZPyCommit(zurl, 0); X(err) // XXX data
fmt.Printf("AAA %s\n", at1)
ctx := context.Background()
stor, err := OpenStorage(ctx, zurl, &OpenOptions{ReadOnly: true}); X(err)
aa, err := stor.LastTid(ctx); X(err)
fmt.Printf("BBB %s\n", aa)
db := NewDB(stor)
txn1, ctx1 := transaction.New(ctx)
conn1, err := db.Open(ctx1, &ConnOptions{}); X(err)
// do not evict oid1 from live cache. oid2 is ok to be evicted.
zcache1 := conn1.Cache()
zcache1.SetControl(&zcacheControl{[]Oid{oid1}})
assert.Equal(conn1.At(), at1)
xobj1, err := conn1.Get(ctx1, oid1); X(err)
xobj2, err := conn1.Get(ctx1, oid2); X(err)
assert.Equal(ClassOf(xobj1), "t.zodb.MyObject")
assert.Equal(ClassOf(xobj2), "t.zodb.MyObject")
obj1 := xobj1.(*MyObject)
obj2 := xobj2.(*MyObject)
checkObj(obj1, conn1, oid1, InvalidTid, GHOST, 0, nil)
checkObj(obj2, conn1, oid2, InvalidTid, GHOST, 0, nil)
// activate: jar has to load, state changes -> uptodate
err = obj1.PActivate(ctx1); X(err)
err = obj2.PActivate(ctx1); X(err)
checkObj(obj1, conn1, oid1, at1, UPTODATE, 1, nil)
checkObj(obj2, conn1, oid2, at1, UPTODATE, 1, nil)
// activate again: refcnt++
err = obj1.PActivate(ctx1); X(err)
err = obj2.PActivate(ctx1); X(err)
checkObj(obj1, conn1, oid1, at1, UPTODATE, 2, nil)
checkObj(obj2, conn1, oid2, at1, UPTODATE, 2, nil)
// deactivate: refcnt--
obj1.PDeactivate()
obj2.PDeactivate()
checkObj(obj1, conn1, oid1, at1, UPTODATE, 1, nil)
checkObj(obj2, conn1, oid2, at1, UPTODATE, 1, nil)
// deactivate: state dropped for obj2, obj1 stays in live cache
obj1.PDeactivate()
obj2.PDeactivate()
checkObj(obj1, conn1, oid1, at1, UPTODATE, 0, nil)
checkObj(obj2, conn1, oid2, InvalidTid, GHOST, 0, nil)
// invalidate: obj1 state dropped
obj1.PDeactivate()
obj2.PDeactivate()
checkObj(obj1, conn1, oid1, InvalidTid, GHOST, 0, nil)
checkObj(obj2, conn1, oid2, InvalidTid, GHOST, 0, nil)
// TODO activate - jar has to load, state changes // XXX
// TODO activate again - refcnt++ txn1.Abort()
// TODO deactivate - refcnt--
// TODO deactivate - state dropped
// TODO invalidate - must be on txn boundary; state dropped
} }
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