merkledag_test.go 12.3 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 20
	offline "github.com/ipfs/go-ipfs/exchange/offline"
	imp "github.com/ipfs/go-ipfs/importer"
	. "github.com/ipfs/go-ipfs/merkledag"
21
	mdpb "github.com/ipfs/go-ipfs/merkledag/pb"
22
	dstest "github.com/ipfs/go-ipfs/merkledag/test"
23
	uio "github.com/ipfs/go-ipfs/unixfs/io"
Jeromy's avatar
Jeromy committed
24

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

func TestNode(t *testing.T) {

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

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

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

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

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

		SubtestNodeStat(t, n)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
69 70 71 72 73 74
	}

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

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

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

Jeromy's avatar
Jeromy committed
89
	k := n.Cid()
90

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

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

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

113 114 115
type devZero struct{}

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

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

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

Jeromy's avatar
Jeromy committed
132
func runBatchFetchTest(t *testing.T, read io.Reader) {
133
	ctx := context.Background()
134
	var dagservs []ipld.DAGService
135
	for _, bsi := range bstest.Mocks(5) {
136 137
		dagservs = append(dagservs, NewDAGService(bsi))
	}
Jeromy's avatar
Jeromy committed
138

139
	spl := chunker.NewSizeSplitter(read, 512)
Jeromy's avatar
Jeromy committed
140

Jeromy's avatar
Jeromy committed
141
	root, err := imp.BuildDagFromReader(dagservs[0], spl)
Jeromy's avatar
Jeromy committed
142 143 144 145
	if err != nil {
		t.Fatal(err)
	}

146 147
	t.Log("finished setup.")

148
	dagr, err := uio.NewDagReader(ctx, root, dagservs[0])
149 150 151
	if err != nil {
		t.Fatal(err)
	}
Jeromy's avatar
Jeromy committed
152 153

	expected, err := ioutil.ReadAll(dagr)
154 155 156 157
	if err != nil {
		t.Fatal(err)
	}

158
	err = dagservs[0].Add(ctx, root)
159 160 161 162 163 164
	if err != nil {
		t.Fatal(err)
	}

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

Jeromy's avatar
Jeromy committed
165
	c := root.Cid()
166

167
	wg := sync.WaitGroup{}
168 169
	errs := make(chan error)

170
	for i := 1; i < len(dagservs); i++ {
171
		wg.Add(1)
172
		go func(i int) {
173
			defer wg.Done()
Jeromy's avatar
Jeromy committed
174
			first, err := dagservs[i].Get(ctx, c)
175
			if err != nil {
176
				errs <- err
177 178 179
			}
			fmt.Println("Got first node back.")

180 181 182 183 184 185
			firstpb, ok := first.(*ProtoNode)
			if !ok {
				errs <- ErrNotProtobuf
			}

			read, err := uio.NewDagReader(ctx, firstpb, dagservs[i])
186
			if err != nil {
187
				errs <- err
188 189 190
			}
			datagot, err := ioutil.ReadAll(read)
			if err != nil {
191
				errs <- err
192 193 194
			}

			if !bytes.Equal(datagot, expected) {
195
				errs <- errors.New("Got bad data back!")
196 197 198 199
			}
		}(i)
	}

200 201 202 203 204 205 206 207 208 209
	go func() {
		wg.Wait()
		close(errs)
	}()

	for err := range errs {
		if err != nil {
			t.Fatal(err)
		}
	}
210
}
211 212

func TestCantGet(t *testing.T) {
213
	ds := dstest.Mock()
214
	a := NodeWithData([]byte("A"))
215

Jeromy's avatar
Jeromy committed
216 217
	c := a.Cid()
	_, err := ds.Get(context.Background(), c)
218 219 220 221
	if !strings.Contains(err.Error(), "not found") {
		t.Fatal("expected err not found, got: ", err)
	}
}
222 223

func TestFetchGraph(t *testing.T) {
224
	var dservs []ipld.DAGService
225
	bsis := bstest.Mocks(2)
Jeromy's avatar
Jeromy committed
226 227 228
	for _, bsi := range bsis {
		dservs = append(dservs, NewDAGService(bsi))
	}
229 230

	read := io.LimitReader(u.NewTimeSeededRand(), 1024*32)
231
	root, err := imp.BuildDagFromReader(dservs[0], chunker.NewSizeSplitter(read, 512))
232 233 234 235
	if err != nil {
		t.Fatal(err)
	}

236
	err = FetchGraph(context.TODO(), root.Cid(), dservs[1])
237 238 239 240
	if err != nil {
		t.Fatal(err)
	}

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

244
	offlineDS := NewDAGService(bs)
Jeromy's avatar
Jeromy committed
245

246
	err = EnumerateChildren(context.Background(), offlineDS.GetLinks, root.Cid(), func(_ *cid.Cid) bool { return true })
247 248 249 250 251 252
	if err != nil {
		t.Fatal(err)
	}
}

func TestEnumerateChildren(t *testing.T) {
253
	bsi := bstest.Mocks(1)
254 255 256
	ds := NewDAGService(bsi[0])

	read := io.LimitReader(u.NewTimeSeededRand(), 1024*1024)
257
	root, err := imp.BuildDagFromReader(ds, chunker.NewSizeSplitter(read, 512))
258 259 260 261
	if err != nil {
		t.Fatal(err)
	}

Jeromy's avatar
Jeromy committed
262
	set := cid.NewSet()
263

264
	err = EnumerateChildren(context.Background(), ds.GetLinks, root.Cid(), set.Visit)
265 266 267 268
	if err != nil {
		t.Fatal(err)
	}

269 270
	var traverse func(n ipld.Node)
	traverse = func(n ipld.Node) {
271
		// traverse dag and check
272 273
		for _, lnk := range n.Links() {
			c := lnk.Cid
Jeromy's avatar
Jeromy committed
274
			if !set.Has(c) {
275
				t.Fatal("missing key in set! ", lnk.Cid.String())
276
			}
Jeromy's avatar
Jeromy committed
277
			child, err := ds.Get(context.Background(), c)
278 279 280 281 282 283 284 285 286
			if err != nil {
				t.Fatal(err)
			}
			traverse(child)
		}
	}

	traverse(root)
}
287 288

func TestFetchFailure(t *testing.T) {
289 290
	ctx := context.Background()

291 292 293
	ds := dstest.Mock()
	ds_bad := dstest.Mock()

294
	top := new(ProtoNode)
295
	for i := 0; i < 10; i++ {
296
		nd := NodeWithData([]byte{byte('a' + i)})
297
		err := ds.Add(ctx, nd)
298 299 300 301 302 303 304 305 306 307 308
		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++ {
309
		nd := NodeWithData([]byte{'f', 'a' + byte(i)})
310
		err := ds_bad.Add(ctx, nd)
311 312 313 314 315 316 317 318 319 320
		if err != nil {
			t.Fatal(err)
		}

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

321
	getters := ipld.GetDAG(ctx, ds, top)
322
	for i, getter := range getters {
323
		_, err := getter.Get(ctx)
324 325 326 327 328 329 330 331
		if err != nil && i < 10 {
			t.Fatal(err)
		}
		if err == nil && i >= 10 {
			t.Fatal("should have failed request")
		}
	}
}
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352

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")
	}

353
	n := &ProtoNode{}
354 355
	n.Marshal()
}
Jeromy's avatar
Jeromy committed
356 357

func TestBasicAddGet(t *testing.T) {
358 359
	ctx := context.Background()

Jeromy's avatar
Jeromy committed
360
	ds := dstest.Mock()
361
	nd := new(ProtoNode)
Jeromy's avatar
Jeromy committed
362

363
	err := ds.Add(ctx, nd)
Jeromy's avatar
Jeromy committed
364 365 366 367
	if err != nil {
		t.Fatal(err)
	}

368
	out, err := ds.Get(ctx, nd.Cid())
Jeromy's avatar
Jeromy committed
369 370 371 372 373 374 375 376
	if err != nil {
		t.Fatal(err)
	}

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

func TestGetRawNodes(t *testing.T) {
379 380
	ctx := context.Background()

381 382 383 384
	rn := NewRawNode([]byte("test"))

	ds := dstest.Mock()

385
	err := ds.Add(ctx, rn)
386 387 388 389
	if err != nil {
		t.Fatal(err)
	}

390
	if !rn.Cid().Equals(rn.Cid()) {
391 392 393
		t.Fatal("output cids didnt match")
	}

394
	out, err := ds.Get(ctx, rn.Cid())
395 396 397 398 399 400 401 402 403 404 405 406
	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")
	}

407
	if out.Tree("", -1) != nil {
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
		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)
437
	nd.SetLinks([]*ipld.Link{{Name: "foo"}})
438

439
	lnk, left, err := nd.ResolveLink([]string{"foo", "bar"})
440 441 442 443 444 445 446 447 448 449 450 451
	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?")
	}

452
	tvals := nd.Tree("", -1)
453 454 455 456
	if len(tvals) != 1 || tvals[0] != "foo" {
		t.Fatal("expected tree to return []{\"foo\"}")
	}
}
457 458

func TestCidRetention(t *testing.T) {
459 460
	ctx := context.Background()

461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
	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()
478
	err = bs.AddBlock(blk)
479 480 481 482 483
	if err != nil {
		t.Fatal(err)
	}

	ds := NewDAGService(bs)
484
	out, err := ds.Get(ctx, c2)
485 486 487 488 489 490 491 492
	if err != nil {
		t.Fatal(err)
	}

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

func TestCidRawDoesnNeedData(t *testing.T) {
495
	srv := NewDAGService(dstest.Bserv())
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
	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")
	}
}
511 512

func TestEnumerateAsyncFailsNotFound(t *testing.T) {
513 514
	ctx := context.Background()

515 516 517 518 519 520
	a := NodeWithData([]byte("foo1"))
	b := NodeWithData([]byte("foo2"))
	c := NodeWithData([]byte("foo3"))
	d := NodeWithData([]byte("foo4"))

	ds := dstest.Mock()
521
	for _, n := range []ipld.Node{a, b, c} {
522
		err := ds.Add(ctx, n)
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544
		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)
	}

545
	err := ds.Add(ctx, parent)
546 547 548 549 550
	if err != nil {
		t.Fatal(err)
	}

	cset := cid.NewSet()
551
	err = EnumerateChildrenAsync(ctx, GetLinksDirect(ds), parent.Cid(), cset.Visit)
552 553 554 555
	if err == nil {
		t.Fatal("this should have failed")
	}
}
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583

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())
	}
}

584
func mkDag(ds ipld.DAGService, depth int) (*cid.Cid, int) {
585 586
	ctx := context.Background()

587 588 589 590 591 592 593
	totalChildren := 0
	f := func() *ProtoNode {
		p := new(ProtoNode)
		buf := make([]byte, 16)
		rand.Read(buf)

		p.SetData(buf)
594
		err := ds.Add(ctx, p)
595 596 597 598 599 600 601 602 603 604
		if err != nil {
			panic(err)
		}
		return p
	}

	for i := 0; i < depth; i++ {
		thisf := f
		f = func() *ProtoNode {
			pn := mkNodeWithChildren(thisf, 10)
605
			err := ds.Add(ctx, pn)
606 607 608 609 610 611 612 613 614
			if err != nil {
				panic(err)
			}
			totalChildren += 10
			return pn
		}
	}

	nd := f()
615
	err := ds.Add(ctx, nd)
616 617 618 619
	if err != nil {
		panic(err)
	}

620
	return nd.Cid(), totalChildren
621 622 623 624 625 626 627 628 629 630 631 632 633 634
}

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
}