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 17
	blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format"

18
	bserv "github.com/ipfs/go-ipfs/blockservice"
19
	bstest "github.com/ipfs/go-ipfs/blockservice/test"
20 21 22 23
	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"
24
	mdpb "github.com/ipfs/go-ipfs/merkledag/pb"
25
	dstest "github.com/ipfs/go-ipfs/merkledag/test"
26
	uio "github.com/ipfs/go-ipfs/unixfs/io"
Jeromy's avatar
Jeromy committed
27

Steven Allen's avatar
Steven Allen committed
28 29
	u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util"
	cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid"
30
	ipld "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-format"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
31 32 33 34
)

func TestNode(t *testing.T) {

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

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

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

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

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

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

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

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

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

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

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

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

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

114 115 116
type devZero struct{}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	read := io.LimitReader(u.NewTimeSeededRand(), 1024*32)
Jeromy's avatar
Jeromy committed
232
	root, err := imp.BuildDagFromReader(dservs[0], chunk.NewSizeSplitter(read, 512))
233 234 235 236
	if err != nil {
		t.Fatal(err)
	}

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

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

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

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

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

	read := io.LimitReader(u.NewTimeSeededRand(), 1024*1024)
Jeromy's avatar
Jeromy committed
258
	root, err := imp.BuildDagFromReader(ds, chunk.NewSizeSplitter(read, 512))
259 260 261 262
	if err != nil {
		t.Fatal(err)
	}

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

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

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

	traverse(root)
}
288 289

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	ds := dstest.Mock()

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

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

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

408
	if out.Tree("", -1) != nil {
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 437
		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)
438
	nd.SetLinks([]*ipld.Link{{Name: "foo"}})
439

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

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

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

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

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

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

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

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

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

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

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

	cset := cid.NewSet()
552
	err = EnumerateChildrenAsync(ctx, GetLinksDirect(ds), parent.Cid(), cset.Visit)
553 554 555 556
	if err == nil {
		t.Fatal("this should have failed")
	}
}
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 584

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

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

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

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

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

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

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

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
}