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

27 28 29
	cid "gx/ipfs/QmV5gPoRsjN1Gid3LMdNZTyfCtP2DsvqEbMAmz82RmmiGk/go-cid"
	node "gx/ipfs/QmYDscK7dmdo2GZ9aumS8s5auUUAH5mR1jvj5pYhWusfK7/go-ipld-node"
	u "gx/ipfs/QmZuY8aV7zbNXVy6DyN9SmnuH3o9nG852F4aTiSBpts8d1/go-ipfs-util"
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

Jeromy's avatar
Jeromy committed
212
func assertCanGet(t *testing.T, ds DAGService, n node.Node) {
Jeromy's avatar
Jeromy committed
213
	if _, err := ds.Get(context.Background(), n.Cid()); err != nil {
214 215 216 217 218
		t.Fatal(err)
	}
}

func TestCantGet(t *testing.T) {
219
	ds := dstest.Mock()
220
	a := NodeWithData([]byte("A"))
221

Jeromy's avatar
Jeromy committed
222 223
	c := a.Cid()
	_, err := ds.Get(context.Background(), c)
224 225 226 227
	if !strings.Contains(err.Error(), "not found") {
		t.Fatal("expected err not found, got: ", err)
	}
}
228 229

func TestFetchGraph(t *testing.T) {
Jeromy's avatar
Jeromy committed
230
	var dservs []DAGService
231
	bsis := bstest.Mocks(2)
Jeromy's avatar
Jeromy committed
232 233 234
	for _, bsi := range bsis {
		dservs = append(dservs, NewDAGService(bsi))
	}
235 236

	read := io.LimitReader(u.NewTimeSeededRand(), 1024*32)
Jeromy's avatar
Jeromy committed
237
	root, err := imp.BuildDagFromReader(dservs[0], chunk.NewSizeSplitter(read, 512))
238 239 240 241
	if err != nil {
		t.Fatal(err)
	}

242
	err = FetchGraph(context.TODO(), root.Cid(), dservs[1])
243 244 245 246
	if err != nil {
		t.Fatal(err)
	}

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

Jeromy's avatar
Jeromy committed
250 251
	offline_ds := NewDAGService(bs)

252
	err = EnumerateChildren(context.Background(), offline_ds.GetLinks, root.Cid(), func(_ *cid.Cid) bool { return true })
253 254 255 256 257 258
	if err != nil {
		t.Fatal(err)
	}
}

func TestEnumerateChildren(t *testing.T) {
259
	bsi := bstest.Mocks(1)
260 261 262
	ds := NewDAGService(bsi[0])

	read := io.LimitReader(u.NewTimeSeededRand(), 1024*1024)
Jeromy's avatar
Jeromy committed
263
	root, err := imp.BuildDagFromReader(ds, chunk.NewSizeSplitter(read, 512))
264 265 266 267
	if err != nil {
		t.Fatal(err)
	}

Jeromy's avatar
Jeromy committed
268
	set := cid.NewSet()
269
	err = EnumerateChildren(context.Background(), ds.GetLinks, root.Cid(), set.Visit)
270 271 272 273
	if err != nil {
		t.Fatal(err)
	}

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

	traverse(root)
}
292 293 294 295 296

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

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

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

356
	n := &ProtoNode{}
357 358
	n.Marshal()
}
Jeromy's avatar
Jeromy committed
359 360 361

func TestBasicAddGet(t *testing.T) {
	ds := dstest.Mock()
362
	nd := new(ProtoNode)
Jeromy's avatar
Jeromy committed
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377

	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")
	}
}
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405

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

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 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 484 485 486 487 488 489

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")
	}
}
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507

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")
	}
}
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 540 541 542 543 544 545 546 547 548 549 550

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()
	err = EnumerateChildrenAsync(context.Background(), ds, pcid, cset.Visit)
	if err == nil {
		t.Fatal("this should have failed")
	}
}
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 622 623 624 625 626 627

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
}