merkledag_test.go 7.81 KB
Newer Older
1
package merkledag_test
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
2 3

import (
4
	"bytes"
5
	"errors"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
6
	"fmt"
7 8
	"io"
	"io/ioutil"
9
	"strings"
10
	"sync"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
11
	"testing"
12

Jeromy's avatar
Jeromy committed
13 14
	ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore"
	dssync "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore/sync"
15
	bstore "github.com/ipfs/go-ipfs/blocks/blockstore"
16
	key "github.com/ipfs/go-ipfs/blocks/key"
17
	bserv "github.com/ipfs/go-ipfs/blockservice"
18
	bstest "github.com/ipfs/go-ipfs/blockservice/test"
19 20 21 22
	offline "github.com/ipfs/go-ipfs/exchange/offline"
	imp "github.com/ipfs/go-ipfs/importer"
	chunk "github.com/ipfs/go-ipfs/importer/chunk"
	. "github.com/ipfs/go-ipfs/merkledag"
23
	dstest "github.com/ipfs/go-ipfs/merkledag/test"
24 25
	"github.com/ipfs/go-ipfs/pin"
	uio "github.com/ipfs/go-ipfs/unixfs/io"
26
	u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util"
Jeromy's avatar
Jeromy committed
27
	"gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
28 29
)

Jeromy's avatar
Jeromy committed
30 31
type dagservAndPinner struct {
	ds DAGService
32
	mp pin.Pinner
Jeromy's avatar
Jeromy committed
33 34
}

jbenet's avatar
jbenet committed
35 36 37 38 39 40 41
func getDagserv(t *testing.T) DAGService {
	db := dssync.MutexWrap(ds.NewMapDatastore())
	bs := bstore.NewBlockstore(db)
	blockserv := bserv.New(bs, offline.Exchange(bs))
	return NewDAGService(blockserv)
}

Jeromy's avatar
Jeromy committed
42
func getDagservAndPinner(t *testing.T) dagservAndPinner {
43 44
	db := dssync.MutexWrap(ds.NewMapDatastore())
	bs := bstore.NewBlockstore(db)
45
	blockserv := bserv.New(bs, offline.Exchange(bs))
Jeromy's avatar
Jeromy committed
46
	dserv := NewDAGService(blockserv)
47
	mpin := pin.NewPinner(db, dserv)
Jeromy's avatar
Jeromy committed
48 49 50 51 52 53
	return dagservAndPinner{
		ds: dserv,
		mp: mpin,
	}
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
func TestNode(t *testing.T) {

	n1 := &Node{Data: []byte("beep")}
	n2 := &Node{Data: []byte("boop")}
	n3 := &Node{Data: []byte("beep boop")}
	if err := n3.AddNodeLink("beep-link", n1); err != nil {
		t.Error(err)
	}
	if err := n3.AddNodeLink("boop-link", n2); err != nil {
		t.Error(err)
	}

	printn := func(name string, n *Node) {
		fmt.Println(">", name)
		fmt.Println("data:", string(n.Data))

		fmt.Println("links:")
		for _, l := range n.Links {
			fmt.Println("-", l.Name, l.Size, l.Hash)
		}

75
		e, err := n.EncodeProtobuf(false)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
76 77 78 79 80 81 82 83 84 85 86 87
		if err != nil {
			t.Error(err)
		} else {
			fmt.Println("encoded:", e)
		}

		h, err := n.Multihash()
		if err != nil {
			t.Error(err)
		} else {
			fmt.Println("hash:", h)
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
88 89 90 91

		k, err := n.Key()
		if err != nil {
			t.Error(err)
92
		} else if k != key.Key(h) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
93 94 95 96
			t.Error("Key is not equivalent to multihash")
		} else {
			fmt.Println("key: ", k)
		}
97 98

		SubtestNodeStat(t, n)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
99 100 101 102 103 104
	}

	printn("beep", n1)
	printn("boop", n2)
	printn("beep boop", n3)
}
105

106
func SubtestNodeStat(t *testing.T, n *Node) {
107
	enc, err := n.EncodeProtobuf(true)
108
	if err != nil {
109
		t.Error("n.EncodeProtobuf(true) failed")
110 111 112 113 114 115 116 117 118
		return
	}

	cumSize, err := n.Size()
	if err != nil {
		t.Error("n.Size() failed")
		return
	}

119 120 121 122 123 124
	k, err := n.Key()
	if err != nil {
		t.Error("n.Key() failed")
		return
	}

125 126 127 128 129 130
	expected := NodeStat{
		NumLinks:       len(n.Links),
		BlockSize:      len(enc),
		LinksSize:      len(enc) - len(n.Data), // includes framing.
		DataSize:       len(n.Data),
		CumulativeSize: int(cumSize),
131
		Hash:           k.B58String(),
132 133 134 135 136 137 138 139
	}

	actual, err := n.Stat()
	if err != nil {
		t.Error("n.Stat() failed")
		return
	}

140
	if expected != *actual {
141
		t.Error("n.Stat incorrect.\nexpect: %s\nactual: %s", expected, actual)
142 143 144 145 146
	} else {
		fmt.Printf("n.Stat correct: %s\n", actual)
	}
}

147 148 149
type devZero struct{}

func (_ devZero) Read(b []byte) (int, error) {
rht's avatar
rht committed
150
	for i := range b {
151 152 153 154 155
		b[i] = 0
	}
	return len(b), nil
}

156
func TestBatchFetch(t *testing.T) {
Jeromy's avatar
Jeromy committed
157 158
	read := io.LimitReader(u.NewTimeSeededRand(), 1024*32)
	runBatchFetchTest(t, read)
159
}
160 161

func TestBatchFetchDupBlock(t *testing.T) {
Jeromy's avatar
Jeromy committed
162 163
	read := io.LimitReader(devZero{}, 1024*32)
	runBatchFetchTest(t, read)
164 165
}

Jeromy's avatar
Jeromy committed
166
func runBatchFetchTest(t *testing.T, read io.Reader) {
167
	ctx := context.Background()
168
	var dagservs []DAGService
169
	for _, bsi := range bstest.Mocks(5) {
170 171
		dagservs = append(dagservs, NewDAGService(bsi))
	}
Jeromy's avatar
Jeromy committed
172

173
	spl := chunk.NewSizeSplitter(read, 512)
Jeromy's avatar
Jeromy committed
174

Jeromy's avatar
Jeromy committed
175
	root, err := imp.BuildDagFromReader(dagservs[0], spl)
Jeromy's avatar
Jeromy committed
176 177 178 179
	if err != nil {
		t.Fatal(err)
	}

180 181
	t.Log("finished setup.")

182
	dagr, err := uio.NewDagReader(ctx, root, dagservs[0])
183 184 185
	if err != nil {
		t.Fatal(err)
	}
Jeromy's avatar
Jeromy committed
186 187

	expected, err := ioutil.ReadAll(dagr)
188 189 190 191
	if err != nil {
		t.Fatal(err)
	}

192
	_, err = dagservs[0].Add(root)
193 194 195 196 197 198 199 200 201 202 203
	if err != nil {
		t.Fatal(err)
	}

	t.Log("Added file to first node.")

	k, err := root.Key()
	if err != nil {
		t.Fatal(err)
	}

204
	wg := sync.WaitGroup{}
205 206
	errs := make(chan error)

207
	for i := 1; i < len(dagservs); i++ {
208
		wg.Add(1)
209
		go func(i int) {
210
			defer wg.Done()
211
			first, err := dagservs[i].Get(ctx, k)
212
			if err != nil {
213
				errs <- err
214 215 216
			}
			fmt.Println("Got first node back.")

217
			read, err := uio.NewDagReader(ctx, first, dagservs[i])
218
			if err != nil {
219
				errs <- err
220 221 222
			}
			datagot, err := ioutil.ReadAll(read)
			if err != nil {
223
				errs <- err
224 225 226
			}

			if !bytes.Equal(datagot, expected) {
227
				errs <- errors.New("Got bad data back!")
228 229 230 231
			}
		}(i)
	}

232 233 234 235 236 237 238 239 240 241
	go func() {
		wg.Wait()
		close(errs)
	}()

	for err := range errs {
		if err != nil {
			t.Fatal(err)
		}
	}
242
}
243 244 245 246 247 248 249

func assertCanGet(t *testing.T, ds DAGService, n *Node) {
	k, err := n.Key()
	if err != nil {
		t.Fatal(err)
	}

250
	if _, err := ds.Get(context.Background(), k); err != nil {
251 252 253 254
		t.Fatal(err)
	}
}

jbenet's avatar
jbenet committed
255 256 257 258 259 260 261 262
func TestEmptyKey(t *testing.T) {
	ds := getDagserv(t)
	_, err := ds.Get(context.Background(), key.Key(""))
	if err != ErrNotFound {
		t.Error("dag service should error when key is nil", err)
	}
}

263 264 265 266 267 268 269 270 271
func TestCantGet(t *testing.T) {
	dsp := getDagservAndPinner(t)
	a := &Node{Data: []byte("A")}

	k, err := a.Key()
	if err != nil {
		t.Fatal(err)
	}

272
	_, err = dsp.ds.Get(context.Background(), k)
273 274 275 276
	if !strings.Contains(err.Error(), "not found") {
		t.Fatal("expected err not found, got: ", err)
	}
}
277 278

func TestFetchGraph(t *testing.T) {
Jeromy's avatar
Jeromy committed
279
	var dservs []DAGService
280
	bsis := bstest.Mocks(2)
Jeromy's avatar
Jeromy committed
281 282 283
	for _, bsi := range bsis {
		dservs = append(dservs, NewDAGService(bsi))
	}
284 285

	read := io.LimitReader(u.NewTimeSeededRand(), 1024*32)
Jeromy's avatar
Jeromy committed
286
	root, err := imp.BuildDagFromReader(dservs[0], chunk.NewSizeSplitter(read, 512))
287 288 289 290
	if err != nil {
		t.Fatal(err)
	}

Jeromy's avatar
Jeromy committed
291
	err = FetchGraph(context.TODO(), root, dservs[1])
292 293 294 295
	if err != nil {
		t.Fatal(err)
	}

Jeromy's avatar
Jeromy committed
296
	// create an offline dagstore and ensure all blocks were fetched
297
	bs := bserv.New(bsis[1].Blockstore, offline.Exchange(bsis[1].Blockstore))
298

Jeromy's avatar
Jeromy committed
299 300 301 302
	offline_ds := NewDAGService(bs)
	ks := key.NewKeySet()

	err = EnumerateChildren(context.Background(), offline_ds, root, ks)
303 304 305 306 307 308
	if err != nil {
		t.Fatal(err)
	}
}

func TestEnumerateChildren(t *testing.T) {
309
	bsi := bstest.Mocks(1)
310 311 312
	ds := NewDAGService(bsi[0])

	read := io.LimitReader(u.NewTimeSeededRand(), 1024*1024)
Jeromy's avatar
Jeromy committed
313
	root, err := imp.BuildDagFromReader(ds, chunk.NewSizeSplitter(read, 512))
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
	if err != nil {
		t.Fatal(err)
	}

	ks := key.NewKeySet()
	err = EnumerateChildren(context.Background(), ds, root, ks)
	if err != nil {
		t.Fatal(err)
	}

	var traverse func(n *Node)
	traverse = func(n *Node) {
		// traverse dag and check
		for _, lnk := range n.Links {
			k := key.Key(lnk.Hash)
			if !ks.Has(k) {
				t.Fatal("missing key in set!")
			}
			child, err := ds.Get(context.Background(), k)
			if err != nil {
				t.Fatal(err)
			}
			traverse(child)
		}
	}

	traverse(root)
}
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384

func TestFetchFailure(t *testing.T) {
	ds := dstest.Mock()
	ds_bad := dstest.Mock()

	top := new(Node)
	for i := 0; i < 10; i++ {
		nd := &Node{Data: []byte{byte('a' + i)}}
		_, err := ds.Add(nd)
		if err != nil {
			t.Fatal(err)
		}

		err = top.AddNodeLinkClean(fmt.Sprintf("AA%d", i), nd)
		if err != nil {
			t.Fatal(err)
		}
	}

	for i := 0; i < 10; i++ {
		nd := &Node{Data: []byte{'f', 'a' + byte(i)}}
		_, err := ds_bad.Add(nd)
		if err != nil {
			t.Fatal(err)
		}

		err = top.AddNodeLinkClean(fmt.Sprintf("BB%d", i), nd)
		if err != nil {
			t.Fatal(err)
		}
	}

	getters := GetDAG(context.Background(), ds, top)
	for i, getter := range getters {
		_, err := getter.Get(context.Background())
		if err != nil && i < 10 {
			t.Fatal(err)
		}
		if err == nil && i >= 10 {
			t.Fatal("should have failed request")
		}
	}
}