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 21
	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"
22
	mdpb "github.com/ipfs/go-ipfs/merkledag/pb"
23
	dstest "github.com/ipfs/go-ipfs/merkledag/test"
24
	uio "github.com/ipfs/go-ipfs/unixfs/io"
Steven Allen's avatar
Steven Allen committed
25
	blocks "gx/ipfs/Qmej7nf81hi2x2tvjRBF3mcp74sQyuDH4VMYDGd1YtXjb2/go-block-format"
Jeromy's avatar
Jeromy committed
26

Steven Allen's avatar
Steven Allen committed
27 28 29
	u "gx/ipfs/QmNiJuT8Ja3hMVpBHXv3Q6dwmperaQ6JjLtpMQgMCD7xvx/go-ipfs-util"
	cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid"
	node "gx/ipfs/Qme5bWv7wtjUNGsK2BNGVUFPKiuxWrsqrtvYwCLRw8YFES/go-ipld-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

Jeromy's avatar
Jeromy committed
91
	expected := node.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 []node.DAGService
135
	for _, bsi := range bstest.Mocks(5) {
136 137
		dagservs = append(dagservs, NewDAGService(bsi))
	}
Jeromy's avatar
Jeromy committed
138

139
	spl := chunk.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 []node.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)
Jeromy's avatar
Jeromy committed
231
	root, err := imp.BuildDagFromReader(dservs[0], chunk.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

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

246
	err = EnumerateChildren(context.Background(), offline_ds.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)
Jeromy's avatar
Jeromy committed
257
	root, err := imp.BuildDagFromReader(ds, chunk.NewSizeSplitter(read, 512))
258 259 260 261
	if err != nil {
		t.Fatal(err)
	}

Jeromy's avatar
Jeromy committed
262
	set := cid.NewSet()
263
	err = EnumerateChildren(context.Background(), ds.GetLinks, root.Cid(), set.Visit)
264 265 266 267
	if err != nil {
		t.Fatal(err)
	}

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

	traverse(root)
}
286 287

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	ds := dstest.Mock()

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

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

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

406
	if out.Tree("", -1) != nil {
407 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 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)
	nd.SetLinks([]*node.Link{{Name: "foo"}})

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

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

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

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

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

	if !out.Cid().Equals(c2) {
		t.Fatal("output cid didnt match")
	}
}
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509

func TestCidRawDoesnNeedData(t *testing.T) {
	srv := NewDAGService(dstest.Bserv())
	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")
	}
}
510 511

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

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

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

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

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

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

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

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

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

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

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

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
}