merkledag_test.go 12.1 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"
25
	blocks "gx/ipfs/QmXxGS5QsUxpR3iqL5DjmsYPHR1Yz74siRQ4ChJqWFosMh/go-block-format"
Jeromy's avatar
Jeromy committed
26

27
	node "gx/ipfs/QmPAKbSsgEX5B6fpmxa61jXYnoWzZr5sNafd3qgPiSH8Uv/go-ipld-format"
28
	u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util"
29
	cid "gx/ipfs/Qma4RJSuh7mMeJQYCqMbKzekn6EwBo7HEs5AQYjVRMQATB/go-cid"
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 []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(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) {
Jeromy's avatar
Jeromy committed
224
	var dservs []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 288 289 290

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

291
	top := new(ProtoNode)
292
	for i := 0; i < 10; i++ {
293
		nd := NodeWithData([]byte{byte('a' + i)})
294 295 296 297 298 299 300 301 302 303 304 305
		_, 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++ {
306
		nd := NodeWithData([]byte{'f', 'a' + byte(i)})
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
		_, 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")
		}
	}
}
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349

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

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

func TestBasicAddGet(t *testing.T) {
	ds := dstest.Mock()
356
	nd := new(ProtoNode)
Jeromy's avatar
Jeromy committed
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371

	c, err := ds.Add(nd)
	if err != nil {
		t.Fatal(err)
	}

	out, err := ds.Get(context.Background(), c)
	if err != nil {
		t.Fatal(err)
	}

	if !nd.Cid().Equals(out.Cid()) {
		t.Fatal("output didnt match input")
	}
}
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399

func TestGetRawNodes(t *testing.T) {
	rn := NewRawNode([]byte("test"))

	ds := dstest.Mock()

	c, err := ds.Add(rn)
	if err != nil {
		t.Fatal(err)
	}

	if !c.Equals(rn.Cid()) {
		t.Fatal("output cids didnt match")
	}

	out, err := ds.Get(context.TODO(), c)
	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")
	}

400
	if out.Tree("", -1) != nil {
401 402 403 404 405 406 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
		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"}})

432
	lnk, left, err := nd.ResolveLink([]string{"foo", "bar"})
433 434 435 436 437 438 439 440 441 442 443 444
	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?")
	}

445
	tvals := nd.Tree("", -1)
446 447 448 449
	if len(tvals) != 1 || tvals[0] != "foo" {
		t.Fatal("expected tree to return []{\"foo\"}")
	}
}
450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483

func TestCidRetention(t *testing.T) {
	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()
	_, err = bs.AddBlock(blk)
	if err != nil {
		t.Fatal(err)
	}

	ds := NewDAGService(bs)
	out, err := ds.Get(context.Background(), c2)
	if err != nil {
		t.Fatal(err)
	}

	if !out.Cid().Equals(c2) {
		t.Fatal("output cid didnt match")
	}
}
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501

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")
	}
}
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539

func TestEnumerateAsyncFailsNotFound(t *testing.T) {
	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} {
		_, err := ds.Add(n)
		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)
	}

	pcid, err := ds.Add(parent)
	if err != nil {
		t.Fatal(err)
	}

	cset := cid.NewSet()
540
	err = EnumerateChildrenAsync(context.Background(), GetLinksDirect(ds), pcid, cset.Visit)
541 542 543 544
	if err == nil {
		t.Fatal("this should have failed")
	}
}
545 546 547 548 549 550 551 552 553 554 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 583 584 585 586 587 588 589 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 618 619 620 621

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

func mkDag(ds DAGService, depth int) (*cid.Cid, int) {
	totalChildren := 0
	f := func() *ProtoNode {
		p := new(ProtoNode)
		buf := make([]byte, 16)
		rand.Read(buf)

		p.SetData(buf)
		_, err := ds.Add(p)
		if err != nil {
			panic(err)
		}
		return p
	}

	for i := 0; i < depth; i++ {
		thisf := f
		f = func() *ProtoNode {
			pn := mkNodeWithChildren(thisf, 10)
			_, err := ds.Add(pn)
			if err != nil {
				panic(err)
			}
			totalChildren += 10
			return pn
		}
	}

	nd := f()
	c, err := ds.Add(nd)
	if err != nil {
		panic(err)
	}

	return c, totalChildren
}

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
}