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

import (
4
	"bytes"
Jeromy's avatar
Jeromy committed
5
	"context"
6
	"errors"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
7
	"fmt"
8 9
	"io"
	"io/ioutil"
10
	"math/rand"
11
	"strings"
12
	"sync"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
13
	"testing"
14
	"time"
15

16
	bserv "github.com/ipfs/go-ipfs/blockservice"
17
	bstest "github.com/ipfs/go-ipfs/blockservice/test"
18 19
	offline "github.com/ipfs/go-ipfs/exchange/offline"
	. "github.com/ipfs/go-ipfs/merkledag"
20
	mdpb "github.com/ipfs/go-ipfs/merkledag/pb"
21
	dstest "github.com/ipfs/go-ipfs/merkledag/test"
Jeromy's avatar
Jeromy committed
22

Steven Allen's avatar
Steven Allen committed
23 24
	u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util"
	cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid"
25
	ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format"
26
	blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
27 28 29 30
)

func TestNode(t *testing.T) {

31 32 33
	n1 := NodeWithData([]byte("beep"))
	n2 := NodeWithData([]byte("boop"))
	n3 := NodeWithData([]byte("beep boop"))
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
34 35 36 37 38 39 40
	if err := n3.AddNodeLink("beep-link", n1); err != nil {
		t.Error(err)
	}
	if err := n3.AddNodeLink("boop-link", n2); err != nil {
		t.Error(err)
	}

41
	printn := func(name string, n *ProtoNode) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
42
		fmt.Println(">", name)
43
		fmt.Println("data:", string(n.Data()))
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
44 45

		fmt.Println("links:")
46 47
		for _, l := range n.Links() {
			fmt.Println("-", l.Name, l.Size, l.Cid)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
48 49
		}

50
		e, err := n.EncodeProtobuf(false)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
51 52 53 54 55 56
		if err != nil {
			t.Error(err)
		} else {
			fmt.Println("encoded:", e)
		}

Jeromy's avatar
Jeromy committed
57
		h := n.Multihash()
Jeromy's avatar
Jeromy committed
58 59
		k := n.Cid().Hash()
		if k.String() != h.String() {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
60 61 62 63
			t.Error("Key is not equivalent to multihash")
		} else {
			fmt.Println("key: ", k)
		}
64 65

		SubtestNodeStat(t, n)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
66 67 68 69 70 71
	}

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

73
func SubtestNodeStat(t *testing.T, n *ProtoNode) {
74
	enc, err := n.EncodeProtobuf(true)
75
	if err != nil {
76
		t.Error("n.EncodeProtobuf(true) failed")
77 78 79 80 81 82 83 84 85
		return
	}

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

Jeromy's avatar
Jeromy committed
86
	k := n.Cid()
87

88
	expected := ipld.NodeStat{
89
		NumLinks:       len(n.Links()),
90
		BlockSize:      len(enc),
91 92
		LinksSize:      len(enc) - len(n.Data()), // includes framing.
		DataSize:       len(n.Data()),
93
		CumulativeSize: int(cumSize),
Jeromy's avatar
Jeromy committed
94
		Hash:           k.String(),
95 96 97 98 99 100 101 102
	}

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

103
	if expected != *actual {
104
		t.Errorf("n.Stat incorrect.\nexpect: %s\nactual: %s", expected, actual)
105 106 107 108 109
	} else {
		fmt.Printf("n.Stat correct: %s\n", actual)
	}
}

110 111 112
type devZero struct{}

func (_ devZero) Read(b []byte) (int, error) {
rht's avatar
rht committed
113
	for i := range b {
114 115 116 117 118
		b[i] = 0
	}
	return len(b), nil
}

119
func TestBatchFetch(t *testing.T) {
Jeromy's avatar
Jeromy committed
120 121
	read := io.LimitReader(u.NewTimeSeededRand(), 1024*32)
	runBatchFetchTest(t, read)
122
}
123 124

func TestBatchFetchDupBlock(t *testing.T) {
Jeromy's avatar
Jeromy committed
125 126
	read := io.LimitReader(devZero{}, 1024*32)
	runBatchFetchTest(t, read)
127 128
}

129 130 131 132 133 134 135
// makeTestDAG creates a simple DAG from the data in a reader.
// First, a node is created from each 512 bytes of data from the reader
// (like a the Size chunker would do). Then all nodes are added as children
// to a root node, which is returned.
func makeTestDAG(t *testing.T, read io.Reader, ds ipld.DAGService) ipld.Node {
	p := make([]byte, 512)
	nodes := []*ProtoNode{}
Hector Sanjuan's avatar
Hector Sanjuan committed
136
	var err error
137
	_, err = io.ReadFull(read, p)
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
	for err == nil {
		protoNode := NodeWithData(p)
		nodes = append(nodes, protoNode)
		_, err = read.Read(p)
	}

	if err != io.EOF {
		t.Fatal(err)
	}

	ctx := context.Background()
	// Add a root referencing all created nodes
	root := NodeWithData(nil)
	for _, n := range nodes {
		root.AddNodeLink(n.Cid().String(), n)
		err := ds.Add(ctx, n)
		if err != nil {
			t.Fatal(err)
		}
	}
	err = ds.Add(ctx, root)
	if err != nil {
		t.Fatal(err)
	}
	return root
}

// makeTestDAGReader takes the root node as returned by makeTestDAG and
// provides a reader that reads all the RawData from that node and its children.
func makeTestDAGReader(t *testing.T, root ipld.Node, ds ipld.DAGService) io.Reader {
	ctx := context.Background()
	buf := new(bytes.Buffer)
	buf.Write(root.RawData())
	for _, l := range root.Links() {
		n, err := ds.Get(ctx, l.Cid)
		if err != nil {
			t.Fatal(err)
		}
		_, err = buf.Write(n.RawData())
		if err != nil {
			t.Fatal(err)
		}
	}
	return buf
}

Jeromy's avatar
Jeromy committed
184
func runBatchFetchTest(t *testing.T, read io.Reader) {
185
	ctx := context.Background()
186
	var dagservs []ipld.DAGService
187
	for _, bsi := range bstest.Mocks(5) {
188 189
		dagservs = append(dagservs, NewDAGService(bsi))
	}
Jeromy's avatar
Jeromy committed
190

191
	root := makeTestDAG(t, read, dagservs[0])
Jeromy's avatar
Jeromy committed
192

193 194
	t.Log("finished setup.")

195
	dagr := makeTestDAGReader(t, root, dagservs[0])
Jeromy's avatar
Jeromy committed
196 197

	expected, err := ioutil.ReadAll(dagr)
198 199 200 201
	if err != nil {
		t.Fatal(err)
	}

202
	err = dagservs[0].Add(ctx, root)
203 204 205 206 207 208
	if err != nil {
		t.Fatal(err)
	}

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

Jeromy's avatar
Jeromy committed
209
	c := root.Cid()
210

211
	wg := sync.WaitGroup{}
212 213
	errs := make(chan error)

214
	for i := 1; i < len(dagservs); i++ {
215
		wg.Add(1)
216
		go func(i int) {
217
			defer wg.Done()
Jeromy's avatar
Jeromy committed
218
			first, err := dagservs[i].Get(ctx, c)
219
			if err != nil {
220
				errs <- err
221 222 223
			}
			fmt.Println("Got first node back.")

224 225 226 227
			firstpb, ok := first.(*ProtoNode)
			if !ok {
				errs <- ErrNotProtobuf
			}
228
			read := makeTestDAGReader(t, firstpb, dagservs[i])
229 230
			datagot, err := ioutil.ReadAll(read)
			if err != nil {
231
				errs <- err
232 233 234
			}

			if !bytes.Equal(datagot, expected) {
235
				errs <- errors.New("Got bad data back!")
236 237 238 239
			}
		}(i)
	}

240 241 242 243 244 245 246 247 248 249
	go func() {
		wg.Wait()
		close(errs)
	}()

	for err := range errs {
		if err != nil {
			t.Fatal(err)
		}
	}
250
}
251 252

func TestCantGet(t *testing.T) {
253
	ds := dstest.Mock()
254
	a := NodeWithData([]byte("A"))
255

Jeromy's avatar
Jeromy committed
256 257
	c := a.Cid()
	_, err := ds.Get(context.Background(), c)
258 259 260 261
	if !strings.Contains(err.Error(), "not found") {
		t.Fatal("expected err not found, got: ", err)
	}
}
262 263

func TestFetchGraph(t *testing.T) {
264
	var dservs []ipld.DAGService
265
	bsis := bstest.Mocks(2)
Jeromy's avatar
Jeromy committed
266 267 268
	for _, bsi := range bsis {
		dservs = append(dservs, NewDAGService(bsi))
	}
269 270

	read := io.LimitReader(u.NewTimeSeededRand(), 1024*32)
271
	root := makeTestDAG(t, read, dservs[0])
272

273
	err := FetchGraph(context.TODO(), root.Cid(), dservs[1])
274 275 276 277
	if err != nil {
		t.Fatal(err)
	}

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

281
	offlineDS := NewDAGService(bs)
Jeromy's avatar
Jeromy committed
282

283
	err = EnumerateChildren(context.Background(), offlineDS.GetLinks, root.Cid(), func(_ *cid.Cid) bool { return true })
284 285 286 287 288 289
	if err != nil {
		t.Fatal(err)
	}
}

func TestEnumerateChildren(t *testing.T) {
290
	bsi := bstest.Mocks(1)
291 292 293
	ds := NewDAGService(bsi[0])

	read := io.LimitReader(u.NewTimeSeededRand(), 1024*1024)
294
	root := makeTestDAG(t, read, ds)
295

Jeromy's avatar
Jeromy committed
296
	set := cid.NewSet()
297

298
	err := EnumerateChildren(context.Background(), ds.GetLinks, root.Cid(), set.Visit)
299 300 301 302
	if err != nil {
		t.Fatal(err)
	}

303 304
	var traverse func(n ipld.Node)
	traverse = func(n ipld.Node) {
305
		// traverse dag and check
306 307
		for _, lnk := range n.Links() {
			c := lnk.Cid
Jeromy's avatar
Jeromy committed
308
			if !set.Has(c) {
309
				t.Fatal("missing key in set! ", lnk.Cid.String())
310
			}
Jeromy's avatar
Jeromy committed
311
			child, err := ds.Get(context.Background(), c)
312 313 314 315 316 317 318 319 320
			if err != nil {
				t.Fatal(err)
			}
			traverse(child)
		}
	}

	traverse(root)
}
321 322

func TestFetchFailure(t *testing.T) {
323 324
	ctx := context.Background()

325 326 327
	ds := dstest.Mock()
	ds_bad := dstest.Mock()

328
	top := new(ProtoNode)
329
	for i := 0; i < 10; i++ {
330
		nd := NodeWithData([]byte{byte('a' + i)})
331
		err := ds.Add(ctx, nd)
332 333 334 335 336 337 338 339 340 341 342
		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++ {
343
		nd := NodeWithData([]byte{'f', 'a' + byte(i)})
344
		err := ds_bad.Add(ctx, nd)
345 346 347 348 349 350 351 352 353 354
		if err != nil {
			t.Fatal(err)
		}

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

355
	getters := ipld.GetDAG(ctx, ds, top)
356
	for i, getter := range getters {
357
		_, err := getter.Get(ctx)
358 359 360 361 362 363 364 365
		if err != nil && i < 10 {
			t.Fatal(err)
		}
		if err == nil && i >= 10 {
			t.Fatal("should have failed request")
		}
	}
}
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386

func TestUnmarshalFailure(t *testing.T) {
	badData := []byte("hello world")

	_, err := DecodeProtobuf(badData)
	if err == nil {
		t.Fatal("shouldnt succeed to parse this")
	}

	// now with a bad link
	pbn := &mdpb.PBNode{Links: []*mdpb.PBLink{{Hash: []byte("not a multihash")}}}
	badlink, err := pbn.Marshal()
	if err != nil {
		t.Fatal(err)
	}

	_, err = DecodeProtobuf(badlink)
	if err == nil {
		t.Fatal("should have failed to parse node with bad link")
	}

387
	n := &ProtoNode{}
388 389
	n.Marshal()
}
Jeromy's avatar
Jeromy committed
390 391

func TestBasicAddGet(t *testing.T) {
392 393
	ctx := context.Background()

Jeromy's avatar
Jeromy committed
394
	ds := dstest.Mock()
395
	nd := new(ProtoNode)
Jeromy's avatar
Jeromy committed
396

397
	err := ds.Add(ctx, nd)
Jeromy's avatar
Jeromy committed
398 399 400 401
	if err != nil {
		t.Fatal(err)
	}

402
	out, err := ds.Get(ctx, nd.Cid())
Jeromy's avatar
Jeromy committed
403 404 405 406 407 408 409 410
	if err != nil {
		t.Fatal(err)
	}

	if !nd.Cid().Equals(out.Cid()) {
		t.Fatal("output didnt match input")
	}
}
411 412

func TestGetRawNodes(t *testing.T) {
413 414
	ctx := context.Background()

415 416 417 418
	rn := NewRawNode([]byte("test"))

	ds := dstest.Mock()

419
	err := ds.Add(ctx, rn)
420 421 422 423
	if err != nil {
		t.Fatal(err)
	}

424
	if !rn.Cid().Equals(rn.Cid()) {
425 426 427
		t.Fatal("output cids didnt match")
	}

428
	out, err := ds.Get(ctx, rn.Cid())
429 430 431 432 433 434 435 436 437 438 439 440
	if err != nil {
		t.Fatal(err)
	}

	if !bytes.Equal(out.RawData(), []byte("test")) {
		t.Fatal("raw block should match input data")
	}

	if out.Links() != nil {
		t.Fatal("raw blocks shouldnt have links")
	}

441
	if out.Tree("", -1) != nil {
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
		t.Fatal("tree should return no paths in a raw block")
	}

	size, err := out.Size()
	if err != nil {
		t.Fatal(err)
	}
	if size != 4 {
		t.Fatal("expected size to be 4")
	}

	ns, err := out.Stat()
	if err != nil {
		t.Fatal(err)
	}

	if ns.DataSize != 4 {
		t.Fatal("expected size to be 4, got: ", ns.DataSize)
	}

	_, _, err = out.Resolve([]string{"foo"})
	if err != ErrLinkNotFound {
		t.Fatal("shouldnt find links under raw blocks")
	}
}

func TestProtoNodeResolve(t *testing.T) {

	nd := new(ProtoNode)
471
	nd.SetLinks([]*ipld.Link{{Name: "foo"}})
472

473
	lnk, left, err := nd.ResolveLink([]string{"foo", "bar"})
474 475 476 477 478 479 480 481 482 483 484 485
	if err != nil {
		t.Fatal(err)
	}

	if len(left) != 1 || left[0] != "bar" {
		t.Fatal("expected the single path element 'bar' to remain")
	}

	if lnk.Name != "foo" {
		t.Fatal("how did we get anything else?")
	}

486
	tvals := nd.Tree("", -1)
487 488 489 490
	if len(tvals) != 1 || tvals[0] != "foo" {
		t.Fatal("expected tree to return []{\"foo\"}")
	}
}
491 492

func TestCidRetention(t *testing.T) {
493 494
	ctx := context.Background()

495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
	nd := new(ProtoNode)
	nd.SetData([]byte("fooooo"))

	pref := nd.Cid().Prefix()
	pref.Version = 1

	c2, err := pref.Sum(nd.RawData())
	if err != nil {
		t.Fatal(err)
	}

	blk, err := blocks.NewBlockWithCid(nd.RawData(), c2)
	if err != nil {
		t.Fatal(err)
	}

	bs := dstest.Bserv()
512
	err = bs.AddBlock(blk)
513 514 515 516 517
	if err != nil {
		t.Fatal(err)
	}

	ds := NewDAGService(bs)
518
	out, err := ds.Get(ctx, c2)
519 520 521 522 523 524 525 526
	if err != nil {
		t.Fatal(err)
	}

	if !out.Cid().Equals(c2) {
		t.Fatal("output cid didnt match")
	}
}
527 528

func TestCidRawDoesnNeedData(t *testing.T) {
529
	srv := NewDAGService(dstest.Bserv())
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544
	nd := NewRawNode([]byte("somedata"))

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	// there is no data for this node in the blockservice
	// so dag service can't load it
	links, err := srv.GetLinks(ctx, nd.Cid())
	if err != nil {
		t.Fatal(err)
	}
	if len(links) != 0 {
		t.Fatal("raw node shouldn't have any links")
	}
}
545 546

func TestEnumerateAsyncFailsNotFound(t *testing.T) {
547 548
	ctx := context.Background()

549 550 551 552 553 554
	a := NodeWithData([]byte("foo1"))
	b := NodeWithData([]byte("foo2"))
	c := NodeWithData([]byte("foo3"))
	d := NodeWithData([]byte("foo4"))

	ds := dstest.Mock()
555
	for _, n := range []ipld.Node{a, b, c} {
556
		err := ds.Add(ctx, n)
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
		if err != nil {
			t.Fatal(err)
		}
	}

	parent := new(ProtoNode)
	if err := parent.AddNodeLinkClean("a", a); err != nil {
		t.Fatal(err)
	}

	if err := parent.AddNodeLinkClean("b", b); err != nil {
		t.Fatal(err)
	}

	if err := parent.AddNodeLinkClean("c", c); err != nil {
		t.Fatal(err)
	}

	if err := parent.AddNodeLinkClean("d", d); err != nil {
		t.Fatal(err)
	}

579
	err := ds.Add(ctx, parent)
580 581 582 583 584
	if err != nil {
		t.Fatal(err)
	}

	cset := cid.NewSet()
585
	err = EnumerateChildrenAsync(ctx, GetLinksDirect(ds), parent.Cid(), cset.Visit)
586 587 588 589
	if err == nil {
		t.Fatal("this should have failed")
	}
}
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617

func TestProgressIndicator(t *testing.T) {
	testProgressIndicator(t, 5)
}

func TestProgressIndicatorNoChildren(t *testing.T) {
	testProgressIndicator(t, 0)
}

func testProgressIndicator(t *testing.T, depth int) {
	ds := dstest.Mock()

	top, numChildren := mkDag(ds, depth)

	v := new(ProgressTracker)
	ctx := v.DeriveContext(context.Background())

	err := FetchGraph(ctx, top, ds)
	if err != nil {
		t.Fatal(err)
	}

	if v.Value() != numChildren+1 {
		t.Errorf("wrong number of children reported in progress indicator, expected %d, got %d",
			numChildren+1, v.Value())
	}
}

618
func mkDag(ds ipld.DAGService, depth int) (*cid.Cid, int) {
619 620
	ctx := context.Background()

621 622 623 624 625 626 627
	totalChildren := 0
	f := func() *ProtoNode {
		p := new(ProtoNode)
		buf := make([]byte, 16)
		rand.Read(buf)

		p.SetData(buf)
628
		err := ds.Add(ctx, p)
629 630 631 632 633 634 635 636 637 638
		if err != nil {
			panic(err)
		}
		return p
	}

	for i := 0; i < depth; i++ {
		thisf := f
		f = func() *ProtoNode {
			pn := mkNodeWithChildren(thisf, 10)
639
			err := ds.Add(ctx, pn)
640 641 642 643 644 645 646 647 648
			if err != nil {
				panic(err)
			}
			totalChildren += 10
			return pn
		}
	}

	nd := f()
649
	err := ds.Add(ctx, nd)
650 651 652 653
	if err != nil {
		panic(err)
	}

654
	return nd.Cid(), totalChildren
655 656 657 658 659 660 661 662 663 664 665 666 667 668
}

func mkNodeWithChildren(getChild func() *ProtoNode, width int) *ProtoNode {
	cur := new(ProtoNode)

	for i := 0; i < width; i++ {
		c := getChild()
		if err := cur.AddNodeLinkClean(fmt.Sprint(i), c); err != nil {
			panic(err)
		}
	}

	return cur
}