package state import ( "testing" "github.com/davecgh/go-spew/spew" "github.com/vapor/protocol/bc" "github.com/vapor/protocol/bc/types" ) func TestCalcPastMedianTime(t *testing.T) { cases := []struct { Timestamps []uint64 MedianTime uint64 }{ { Timestamps: []uint64{1}, MedianTime: 1, }, { Timestamps: []uint64{1, 2}, MedianTime: 2, }, { Timestamps: []uint64{1, 3, 2}, MedianTime: 2, }, { Timestamps: []uint64{1, 3, 2, 3}, MedianTime: 3, }, { Timestamps: []uint64{1, 2, 3, 4, 5, 6, 7, 8, 11, 10, 9}, MedianTime: 6, }, { Timestamps: []uint64{1, 2, 3, 4, 5, 6, 7, 8, 11, 10, 9, 11, 11, 11, 14}, MedianTime: 10, }, } for idx, c := range cases { var parentNode *BlockNode for i := range c.Timestamps { blockHeader := &types.BlockHeader{ Height: uint64(i), Timestamp: c.Timestamps[i], } blockNode, err := NewBlockNode(blockHeader, parentNode) if err != nil { t.Fatal(err) } parentNode = blockNode } medianTime := parentNode.CalcPastMedianTime() if medianTime != c.MedianTime { t.Fatalf("calc median timestamp failed, index: %d, expected: %d, have: %d", idx, c.MedianTime, medianTime) } } } func TestSetMainChain(t *testing.T) { blockIndex := NewBlockIndex() var lastNode *BlockNode for i := uint64(0); i < 4; i++ { node := &BlockNode{ Height: i, Hash: bc.Hash{V0: i}, Parent: lastNode, } blockIndex.AddNode(node) lastNode = node } tailNode := lastNode blockIndex.SetMainChain(lastNode) for lastNode.Parent != nil { if !blockIndex.InMainchain(lastNode.Hash) { t.Fatalf("block %d, hash %v is not in main chain", lastNode.Height, lastNode.Hash) } lastNode = lastNode.Parent } // fork and set main chain forkHeight := uint64(1) lastNode = blockIndex.nodeByHeight(forkHeight) for i := uint64(1); i <= 3; i++ { node := &BlockNode{ Height: lastNode.Height + 1, Hash: bc.Hash{V1: uint64(i)}, Parent: lastNode, } blockIndex.AddNode(node) lastNode = node } bestNode := lastNode blockIndex.SetMainChain(lastNode) for lastNode.Parent != nil { if !blockIndex.InMainchain(lastNode.Hash) { t.Fatalf("after fork, block %d, hash %v is not in main chain", lastNode.Height, lastNode.Hash) } lastNode = lastNode.Parent } if bestNode != blockIndex.BestNode() { t.Fatalf("check best node failed") } for tailNode.Parent != nil && tailNode.Height > forkHeight { if blockIndex.InMainchain(tailNode.Hash) { t.Fatalf("old chain block %d, hash %v still in main chain", tailNode.Height, tailNode.Hash) } tailNode = tailNode.Parent } } // MockBlockIndex will mock a empty BlockIndex func MockBlockIndex() *BlockIndex { return &BlockIndex{ index: make(map[bc.Hash]*BlockNode), mainChain: make([]*BlockNode, 0, 2), } } func TestSetMainChainExtendCap(t *testing.T) { blockIndex := MockBlockIndex() var lastNode *BlockNode cases := []struct { start uint64 stop uint64 wantLen int wantCap int }{ { start: 0, stop: 500, wantLen: 500, wantCap: 500 + approxNodesPerDay, }, { start: 500, stop: 1000, wantLen: 1000, wantCap: 500 + approxNodesPerDay, }, { start: 1000, stop: 2000, wantLen: 2000, wantCap: 2000 + approxNodesPerDay, }, } for num, c := range cases { for i := c.start; i < c.stop; i++ { node := &BlockNode{ Height: i, Hash: bc.Hash{V0: i}, Parent: lastNode, } blockIndex.AddNode(node) lastNode = node } blockIndex.SetMainChain(lastNode) if c.wantLen != len(blockIndex.mainChain) || c.wantCap != cap(blockIndex.mainChain) { t.Fatalf("SetMainChain extended capacity error, index: %d, got len: %d, got cap: %d, want len: %d, want cap: %d", num, len(blockIndex.mainChain), cap(blockIndex.mainChain), c.wantLen, c.wantCap) } } for i := 0; i < len(blockIndex.mainChain); i++ { if blockIndex.mainChain[i] != blockIndex.index[blockIndex.mainChain[i].Hash] { t.Fatal("SetMainChain extended capacity error, index:", i, "want:", spew.Sdump(blockIndex.mainChain[i]), "got:", spew.Sdump(blockIndex.index[blockIndex.mainChain[i].Hash])) } } }