From 44348ada78b2bc8db54c2aaf58ec86861702bf6a Mon Sep 17 00:00:00 2001
From: Kirill Smelkov <kirr@nexedi.com>
Date: Sun, 5 Jul 2020 21:49:43 +0300
Subject: [PATCH] .

---
 go/internal/xtesting/xtesting.go | 65 ++++++++++++++++++++++++++++++++
 go/zodb/storage/zeo/zeo_test.go  | 56 ++++++++++++++++++++++++++-
 2 files changed, 119 insertions(+), 2 deletions(-)

diff --git a/go/internal/xtesting/xtesting.go b/go/internal/xtesting/xtesting.go
index 76260404..52cb6933 100644
--- a/go/internal/xtesting/xtesting.go
+++ b/go/internal/xtesting/xtesting.go
@@ -24,6 +24,7 @@ import (
 	"bytes"
 	"context"
 	"fmt"
+	"io"
 	"net/url"
 	"os"
 	"os/exec"
@@ -149,6 +150,63 @@ type Txn struct {
 	Data   []*zodb.DataInfo
 }
 
+// LoadDB loads whole content of a ZODB database.
+// it returns full history of all transactions with committed data.
+func LoadDB(zurl string) (_ []Txn, err error) {
+	xerr.Contextf(&err, "loaddb %s", zurl)
+	ctx := context.Background()
+	zstor, err := zodb.Open(ctx, zurl, &zodb.OpenOptions{ReadOnly: true})
+	if err != nil {
+		return nil, err
+	}
+	defer func() {
+		err2 := zstor.Close()
+		if err == nil {
+			err = err2
+		}
+	}()
+
+	txnv := []Txn{}
+	it := zstor.Iterate(ctx, 0, zodb.TidMax)
+	for {
+		txni, dataIt, err := it.NextTxn(ctx)
+		if err != nil {
+			if err == io.EOF {
+				break
+			}
+			return nil, err
+		}
+		txn := &Txn{Header: &zodb.TxnInfo{
+			// clone (txni stays valid only until next iteration)
+			Tid:         txni.Tid,
+			Status:      txni.Status,
+			User:        bcopy(txni.User),
+			Description: bcopy(txni.Description),
+			Extension:   bcopy(txni.Extension),
+		}}
+
+		for {
+			datai, err := dataIt.NextData(ctx)
+			if err != nil {
+				if err == io.EOF {
+					break
+				}
+				return nil, err
+			}
+
+			txn.Data = append(txn.Data, &zodb.DataInfo{
+				// clone (datai stays valid only until next iteration)
+				Oid:         datai.Oid,
+				Tid:         datai.Tid,
+				Data:        bcopy(datai.Data),
+				DataTidHint: datai.DataTidHint,
+			})
+		}
+	}
+
+	return txnv, nil
+}
+
 // checkLoad verifies that zdrv.Load(xid) returns expected result
 func checkLoad(t *testing.T, zdrv zodb.IStorageDriver, xid zodb.Xid, expect objState) {
 	t.Helper()
@@ -348,3 +406,10 @@ func DrvTestWatch(t *testing.T, zurl string, zdrvOpen zodb.DriverOpener) {
 func b(data string) []byte {
 	return []byte(data)
 }
+
+// bcopy makes a clone of byte slice.
+func bcopy(data []byte) []byte {
+	d2 := make([]byte, len(data))
+	copy(d2, data)
+	return d2
+}
diff --git a/go/zodb/storage/zeo/zeo_test.go b/go/zodb/storage/zeo/zeo_test.go
index e5aab0ab..1a949a57 100644
--- a/go/zodb/storage/zeo/zeo_test.go
+++ b/go/zodb/storage/zeo/zeo_test.go
@@ -22,11 +22,14 @@ package zeo
 import (
 	"context"
 	"io/ioutil"
+	"net/url"
 	"os"
 	"os/exec"
 	"testing"
 
 	"lab.nexedi.com/kirr/neo/go/internal/xtesting"
+	"lab.nexedi.com/kirr/neo/go/zodb"
+	_ "lab.nexedi.com/kirr/neo/go/zodb/storage/fs1"
 
 	"lab.nexedi.com/kirr/go123/exc"
 	"lab.nexedi.com/kirr/go123/xerr"
@@ -78,14 +81,43 @@ func (z *ZEOPySrv) Close() (err error) {
 }
 
 
-func TestWatch(t *testing.T) {
+// --------
+
+func TestLoad(t *testing.T) {
 	X := exc.Raiseif
-	xtesting.NeedPy(t, "ZEO") // XXX +msgpack?
+	needZEOpy(t)
 
 	work := xtempdir(t)
 	defer os.RemoveAll(work)
+	fs1path := work + "/1.fs"
+
+	// copy ../fs1/testdata/1.fs -> fs1path
+	data, err := ioutil.ReadFile("../fs1/testdata/1.fs");	X(err)
+	err = ioutil.WriteFile(fs1path, data, 0644);		X(err)
+
+	txnvOk, err := xtesting.LoadDB(fs1path); X(err)
+
+	zpy, err := StartZEOPySrv(fs1path); X(err)
+	defer func() {
+		err := zpy.Close(); X(err)
+	}()
+
+	z, _, err := zeoOpen(zpy.zaddr(), &zodb.DriverOptions{ReadOnly: true}); X(err)
+	defer func() {
+		err := z.Close(); X(err)
+	}()
+
+	xtesting.DrvTestLoad(t, z, txnvOk)
+}
+
+func TestWatch(t *testing.T) {
+	X := exc.Raiseif
+	needZEOpy(t)
 
+	work := xtempdir(t)
+	defer os.RemoveAll(work)
 	fs1path := work + "/1.fs"
+
 	zpy, err := StartZEOPySrv(fs1path); X(err)
 	defer func() {
 		err := zpy.Close(); X(err)
@@ -95,6 +127,21 @@ func TestWatch(t *testing.T) {
 }
 
 
+func zeoOpen(zurl string, opt *zodb.DriverOptions) (_ *zeo, at0 zodb.Tid, err error) {
+	defer xerr.Contextf(&err, "openzeo %s", zurl)
+	u, err := url.Parse(zurl)
+	if err != nil {
+		return nil, 0, err
+	}
+
+	z, at0, err := openByURL(context.Background(), u, opt)
+	if err != nil {
+		return nil, 0, err
+	}
+
+	return z.(*zeo), at0, nil
+}
+
 func xtempdir(t *testing.T) string {
 	t.Helper()
 	tmpd, err := ioutil.TempDir("", "zeo")
@@ -103,3 +150,8 @@ func xtempdir(t *testing.T) string {
 	}
 	return tmpd
 }
+
+
+func needZEOpy(t *testing.T) {
+	xtesting.NeedPy(t, "ZEO") // XXX +msgpack?
+}
-- 
2.30.9