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

.

parent 4eb451a4
......@@ -17,7 +17,20 @@
// See COPYING file for full licensing terms.
// 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
// - if obj's type was registered (RegisterClass) -- corresponding class.
// - for Broken objects -- ZODB.Broken("<broken-class>").
// - else -- ZODB.Go("<fully-qualified-type(obj)>")
//
// XXX -> IPersistent.ZClass() ?
func ClassOf(obj IPersistent) string {
zb, broken := obj.(*Broken)
if broken {
......
......@@ -20,10 +20,16 @@
package zodb
import (
"context"
"fmt"
"io/ioutil"
"os"
"reflect"
"testing"
"lab.nexedi.com/kirr/neo/go/transaction"
"lab.nexedi.com/kirr/go123/exc"
"lab.nexedi.com/kirr/go123/mem"
"github.com/stretchr/testify/require"
)
......@@ -62,48 +68,58 @@ func init() {
RegisterClassAlias("t.zodb.MyOldObject", "t.zodb.MyObject")
}
func TestPersistent(t *testing.T) {
assert := require.New(t)
// _checkObj verifies current state of persistent object.
//
// 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.
checkObj := func(obj IPersistent, jar *Connection, oid Oid, serial Tid, state ObjectState, refcnt int32, loading *loadState) {
badf := func(format string, argv ...interface{}) {
t.Helper()
xbase := reflect.ValueOf(obj).Elem().FieldByName("Persistent")
pbase := xbase.Addr().Interface().(*Persistent)
msg := fmt.Sprintf(format, argv...)
t.Fatalf("%#v: %s", obj, msg)
}
badf := func(format string, argv ...interface{}) {
t.Helper()
msg := fmt.Sprintf(format, argv...)
t.Fatalf("%#v: %s", obj, msg)
}
zc := pbase.zclass
//zc.class
if typ := reflect.TypeOf(obj).Elem(); typ != zc.typ {
badf("invalid zclass: .typ = %s ; want %s", zc.typ, typ)
}
//zc.stateType
zc := pbase.zclass
//zc.class
if typ := reflect.TypeOf(obj).Elem(); typ != zc.typ {
badf("invalid zclass: .typ = %s ; want %s", zc.typ, typ)
}
//zc.stateType
if pbase.jar != jar {
badf("invalid jar")
}
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?
}
if pbase.jar != jar {
badf("invalid jar")
}
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?
func tCheckObj(t testing.TB) func(IPersistent, *Connection, Oid, Tid, ObjectState, int32, *loadState) {
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)
}
}
// basic Persistent tests without storage.
func TestPersistentBasic(t *testing.T) {
assert := require.New(t)
checkObj := tCheckObj(t)
// unknown type -> Broken
xobj := newGhost("t.unknown", 10, nil)
......@@ -140,11 +156,102 @@ func TestPersistent(t *testing.T) {
// ClassOf(unregistered-obj)
obj2 := &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
// TODO activate again - refcnt++
// TODO deactivate - refcnt--
// TODO deactivate - state dropped
// TODO invalidate - must be on txn boundary; state dropped
// XXX
txn1.Abort()
}
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