package branchmanager import ( "reflect" "testing" "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/kvstore/mapdb" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) type sampleTree struct { cachedBranches [17]*CachedBranch branches [17]*Branch numID map[BranchID]int } func (st *sampleTree) Release() { for i := range st.cachedBranches { if st.cachedBranches[i] == nil { continue } st.cachedBranches[i].Release() } } // eventMock is a wrapper around mock.Mock used to test the triggered events. type eventMock struct { mock.Mock attached []struct { *events.Event *events.Closure } } func newEventMock(t *testing.T, mgr *BranchManager) *eventMock { e := &eventMock{} e.Test(t) // attach all events e.attach(mgr.Events.BranchConfirmed, e.BranchConfirmed) e.attach(mgr.Events.BranchDisliked, e.BranchDisliked) e.attach(mgr.Events.BranchFinalized, e.BranchFinalized) e.attach(mgr.Events.BranchLiked, e.BranchLiked) e.attach(mgr.Events.BranchPreferred, e.BranchPreferred) e.attach(mgr.Events.BranchRejected, e.BranchRejected) e.attach(mgr.Events.BranchUnpreferred, e.BranchUnpreferred) // assure that all available events are mocked numEvents := reflect.ValueOf(mgr.Events).Elem().NumField() assert.Equalf(t, len(e.attached), numEvents, "not all events in BranchManager.Events have been attached") return e } // DetachAll detaches all attached event mocks. func (e *eventMock) DetachAll() { for _, a := range e.attached { a.Event.Detach(a.Closure) } } // Expect starts a description of an expectation of the specified event being triggered. func (e *eventMock) Expect(eventName string, arguments ...interface{}) { e.On(eventName, arguments...) } func (e *eventMock) attach(event *events.Event, f interface{}) { closure := events.NewClosure(f) event.Attach(closure) e.attached = append(e.attached, struct { *events.Event *events.Closure }{event, closure}) } func (e *eventMock) BranchConfirmed(cachedBranch *CachedBranch) { defer cachedBranch.Release() e.Called(cachedBranch.Unwrap()) } func (e *eventMock) BranchDisliked(cachedBranch *CachedBranch) { defer cachedBranch.Release() e.Called(cachedBranch.Unwrap()) } func (e *eventMock) BranchFinalized(cachedBranch *CachedBranch) { defer cachedBranch.Release() e.Called(cachedBranch.Unwrap()) } func (e *eventMock) BranchLiked(cachedBranch *CachedBranch) { defer cachedBranch.Release() e.Called(cachedBranch.Unwrap()) } func (e *eventMock) BranchPreferred(cachedBranch *CachedBranch) { defer cachedBranch.Release() e.Called(cachedBranch.Unwrap()) } func (e *eventMock) BranchRejected(cachedBranch *CachedBranch) { defer cachedBranch.Release() e.Called(cachedBranch.Unwrap()) } func (e *eventMock) BranchUnpreferred(cachedBranch *CachedBranch) { defer cachedBranch.Release() e.Called(cachedBranch.Unwrap()) } // ./img/sample_tree.png func createSampleTree(branchManager *BranchManager) *sampleTree { st := &sampleTree{ numID: map[BranchID]int{}, } cachedMasterBranch := branchManager.Branch(MasterBranchID) st.cachedBranches[1] = cachedMasterBranch st.branches[1] = cachedMasterBranch.Unwrap() st.numID[st.branches[1].ID()] = 1 cachedBranch2, _ := branchManager.Fork(BranchID{2}, []BranchID{MasterBranchID}, []ConflictID{{0}}) branch2 := cachedBranch2.Unwrap() st.branches[2] = branch2 st.cachedBranches[2] = cachedBranch2 st.numID[st.branches[2].ID()] = 2 cachedBranch3, _ := branchManager.Fork(BranchID{3}, []BranchID{MasterBranchID}, []ConflictID{{0}}) branch3 := cachedBranch3.Unwrap() st.branches[3] = branch3 st.cachedBranches[3] = cachedBranch3 st.numID[st.branches[3].ID()] = 3 cachedBranch4, _ := branchManager.Fork(BranchID{4}, []BranchID{branch2.ID()}, []ConflictID{{1}}) branch4 := cachedBranch4.Unwrap() st.branches[4] = branch4 st.cachedBranches[4] = cachedBranch4 st.numID[st.branches[4].ID()] = 4 cachedBranch5, _ := branchManager.Fork(BranchID{5}, []BranchID{branch2.ID()}, []ConflictID{{1}}) branch5 := cachedBranch5.Unwrap() st.branches[5] = branch5 st.cachedBranches[5] = cachedBranch5 st.numID[st.branches[5].ID()] = 5 cachedBranch6, _ := branchManager.Fork(BranchID{6}, []BranchID{MasterBranchID}, []ConflictID{{2}}) branch6 := cachedBranch6.Unwrap() st.branches[6] = branch6 st.cachedBranches[6] = cachedBranch6 st.numID[st.branches[6].ID()] = 6 cachedBranch7, _ := branchManager.Fork(BranchID{7}, []BranchID{MasterBranchID}, []ConflictID{{2}}) branch7 := cachedBranch7.Unwrap() st.branches[7] = branch7 st.cachedBranches[7] = cachedBranch7 st.numID[st.branches[7].ID()] = 7 cachedBranch8, _ := branchManager.Fork(BranchID{8}, []BranchID{MasterBranchID}, []ConflictID{{3}}) branch8 := cachedBranch8.Unwrap() st.branches[8] = branch8 st.cachedBranches[8] = cachedBranch8 st.numID[st.branches[8].ID()] = 8 cachedBranch9, _ := branchManager.Fork(BranchID{9}, []BranchID{MasterBranchID}, []ConflictID{{3}}) branch9 := cachedBranch9.Unwrap() st.branches[9] = branch9 st.cachedBranches[9] = cachedBranch9 st.numID[st.branches[9].ID()] = 9 cachedAggrBranch10, err := branchManager.AggregateBranches(branch3.ID(), branch6.ID()) if err != nil { panic(err) } aggrBranch10 := cachedAggrBranch10.Unwrap() st.branches[10] = aggrBranch10 st.cachedBranches[10] = cachedAggrBranch10 st.numID[st.branches[10].ID()] = 10 cachedAggrBranch11, err := branchManager.AggregateBranches(branch6.ID(), branch8.ID()) if err != nil { panic(err) } aggrBranch11 := cachedAggrBranch11.Unwrap() st.branches[11] = aggrBranch11 st.cachedBranches[11] = cachedAggrBranch11 st.numID[st.branches[11].ID()] = 11 cachedBranch12, _ := branchManager.Fork(BranchID{12}, []BranchID{aggrBranch10.ID()}, []ConflictID{{4}}) branch12 := cachedBranch12.Unwrap() st.branches[12] = branch12 st.cachedBranches[12] = cachedBranch12 st.numID[st.branches[12].ID()] = 12 cachedBranch13, _ := branchManager.Fork(BranchID{13}, []BranchID{aggrBranch10.ID()}, []ConflictID{{4}}) branch13 := cachedBranch13.Unwrap() st.branches[13] = branch13 st.cachedBranches[13] = cachedBranch13 st.numID[st.branches[13].ID()] = 13 cachedBranch14, _ := branchManager.Fork(BranchID{14}, []BranchID{aggrBranch11.ID()}, []ConflictID{{5}}) branch14 := cachedBranch14.Unwrap() st.branches[14] = branch14 st.cachedBranches[14] = cachedBranch14 st.numID[st.branches[14].ID()] = 14 cachedBranch15, _ := branchManager.Fork(BranchID{15}, []BranchID{aggrBranch11.ID()}, []ConflictID{{5}}) branch15 := cachedBranch15.Unwrap() st.branches[15] = branch15 st.cachedBranches[15] = cachedBranch15 st.numID[st.branches[15].ID()] = 15 cachedAggrBranch16, err := branchManager.AggregateBranches(branch13.ID(), branch14.ID()) if err != nil { panic(err) } aggrBranch16 := cachedAggrBranch16.Unwrap() st.branches[16] = aggrBranch16 st.cachedBranches[16] = cachedAggrBranch16 st.numID[st.branches[16].ID()] = 16 return st } func TestGetAncestorBranches(t *testing.T) { branchManager := New(mapdb.NewMapDB()) st := createSampleTree(branchManager) defer st.Release() { masterBranch := branchManager.Branch(MasterBranchID) assert.NotNil(t, masterBranch) ancestorBranches, err := branchManager.getAncestorBranches(masterBranch.Unwrap()) assert.NoError(t, err) expectedAncestors := map[BranchID]struct{}{} actualAncestors := map[BranchID]struct{}{} ancestorBranches.Consume(func(branch *Branch) { actualAncestors[branch.ID()] = struct{}{} }) assert.Equal(t, expectedAncestors, actualAncestors) } { ancestorBranches, err := branchManager.getAncestorBranches(st.branches[4]) assert.NoError(t, err) expectedAncestors := map[BranchID]struct{}{ st.branches[2].ID(): {}, MasterBranchID: {}, } actualAncestors := map[BranchID]struct{}{} ancestorBranches.Consume(func(branch *Branch) { actualAncestors[branch.ID()] = struct{}{} }) assert.Equal(t, expectedAncestors, actualAncestors) } { ancestorBranches, err := branchManager.getAncestorBranches(st.branches[16]) assert.NoError(t, err) expectedAncestors := map[BranchID]struct{}{ st.branches[13].ID(): {}, st.branches[14].ID(): {}, st.branches[10].ID(): {}, st.branches[11].ID(): {}, st.branches[3].ID(): {}, st.branches[6].ID(): {}, st.branches[8].ID(): {}, MasterBranchID: {}, } actualAncestors := map[BranchID]struct{}{} ancestorBranches.Consume(func(branch *Branch) { actualAncestors[branch.ID()] = struct{}{} }) assert.Equal(t, expectedAncestors, actualAncestors) } { ancestorBranches, err := branchManager.getAncestorBranches(st.branches[12]) assert.NoError(t, err) expectedAncestors := map[BranchID]struct{}{ st.branches[10].ID(): {}, st.branches[3].ID(): {}, st.branches[6].ID(): {}, MasterBranchID: {}, } actualAncestors := map[BranchID]struct{}{} ancestorBranches.Consume(func(branch *Branch) { actualAncestors[branch.ID()] = struct{}{} }) assert.Equal(t, expectedAncestors, actualAncestors) } { ancestorBranches, err := branchManager.getAncestorBranches(st.branches[11]) assert.NoError(t, err) expectedAncestors := map[BranchID]struct{}{ st.branches[8].ID(): {}, st.branches[6].ID(): {}, MasterBranchID: {}, } actualAncestors := map[BranchID]struct{}{} ancestorBranches.Consume(func(branch *Branch) { actualAncestors[branch.ID()] = struct{}{} }) assert.Equal(t, expectedAncestors, actualAncestors) } } func TestIsAncestorOfBranch(t *testing.T) { branchManager := New(mapdb.NewMapDB()) st := createSampleTree(branchManager) defer st.Release() type testcase struct { ancestor *Branch descendent *Branch is bool } cases := []testcase{ {st.branches[2], st.branches[4], true}, {st.branches[4], st.branches[2], false}, {st.branches[3], st.branches[4], false}, {st.branches[3], st.branches[12], true}, {st.branches[3], st.branches[10], true}, {st.branches[3], st.branches[16], true}, {st.branches[1], st.branches[16], true}, {st.branches[11], st.branches[16], true}, {st.branches[9], st.branches[16], false}, {st.branches[6], st.branches[15], true}, } for _, testCase := range cases { isAncestor, err := branchManager.branchIsAncestorOfBranch(testCase.ancestor, testCase.descendent) assert.NoError(t, err) if testCase.is { assert.True(t, isAncestor, "branch %d is an ancestor of branch %d", st.numID[testCase.ancestor.ID()], st.numID[testCase.descendent.ID()]) continue } assert.False(t, isAncestor, "branch %d is not an ancestor of branch %d", st.numID[testCase.ancestor.ID()], st.numID[testCase.descendent.ID()]) } } func TestFindDeepestCommonDescendants(t *testing.T) { branchManager := New(mapdb.NewMapDB()) st := createSampleTree(branchManager) defer st.Release() { deepestCommonDescendants, err := branchManager.findDeepestCommonDescendants(MasterBranchID, st.branches[2].ID()) assert.NoError(t, err) expectedDeepestCommonDescendants := map[BranchID]struct{}{ st.branches[2].ID(): {}, } actualDeepestCommonDescendants := map[BranchID]struct{}{} deepestCommonDescendants.Consume(func(branch *Branch) { actualDeepestCommonDescendants[branch.ID()] = struct{}{} }) assert.Equal(t, expectedDeepestCommonDescendants, actualDeepestCommonDescendants) } { deepestCommonDescendants, err := branchManager.findDeepestCommonDescendants(st.branches[2].ID(), st.branches[3].ID()) assert.NoError(t, err) expectedDeepestCommonDescendants := map[BranchID]struct{}{ st.branches[2].ID(): {}, st.branches[3].ID(): {}, } actualDeepestCommonDescendants := map[BranchID]struct{}{} deepestCommonDescendants.Consume(func(branch *Branch) { actualDeepestCommonDescendants[branch.ID()] = struct{}{} }) assert.Equal(t, expectedDeepestCommonDescendants, actualDeepestCommonDescendants) } { deepestCommonDescendants, err := branchManager.findDeepestCommonDescendants(st.branches[2].ID(), st.branches[4].ID(), st.branches[5].ID()) assert.NoError(t, err) expectedDeepestCommonDescendants := map[BranchID]struct{}{ st.branches[4].ID(): {}, st.branches[5].ID(): {}, } actualDeepestCommonDescendants := map[BranchID]struct{}{} deepestCommonDescendants.Consume(func(branch *Branch) { actualDeepestCommonDescendants[branch.ID()] = struct{}{} }) assert.Equal(t, expectedDeepestCommonDescendants, actualDeepestCommonDescendants) } // breaks: should only be aggr. branch 11 which consists out of branch 6 and 8 { deepestCommonDescendants, err := branchManager.findDeepestCommonDescendants( st.branches[6].ID(), st.branches[8].ID(), st.branches[11].ID()) assert.NoError(t, err) expectedDeepestCommonDescendants := map[BranchID]struct{}{ st.branches[11].ID(): {}, } actualDeepestCommonDescendants := map[BranchID]struct{}{} deepestCommonDescendants.Consume(func(branch *Branch) { actualDeepestCommonDescendants[branch.ID()] = struct{}{} }) assert.Equal(t, expectedDeepestCommonDescendants, actualDeepestCommonDescendants) } // breaks: thinks that branch 13 is one of the deepest common descendants { deepestCommonDescendants, err := branchManager.findDeepestCommonDescendants( st.branches[6].ID(), st.branches[8].ID(), st.branches[10].ID(), st.branches[11].ID(), st.branches[12].ID(), st.branches[15].ID(), st.branches[14].ID(), st.branches[13].ID(), st.branches[16].ID()) assert.NoError(t, err) expectedDeepestCommonDescendants := map[BranchID]struct{}{ st.branches[12].ID(): {}, st.branches[15].ID(): {}, st.branches[16].ID(): {}, } actualDeepestCommonDescendants := map[BranchID]struct{}{} deepestCommonDescendants.Consume(func(branch *Branch) { actualDeepestCommonDescendants[branch.ID()] = struct{}{} }) assert.Equal(t, expectedDeepestCommonDescendants, actualDeepestCommonDescendants) } } func TestCollectClosestConflictAncestors(t *testing.T) { branchManager := New(mapdb.NewMapDB()) st := createSampleTree(branchManager) defer st.Release() { aggregatedBranchConflictParents := make(CachedBranches) err := branchManager.collectClosestConflictAncestors(st.branches[10], aggregatedBranchConflictParents) assert.NoError(t, err) expectedClosestConflictAncestors := map[BranchID]struct{}{ st.branches[3].ID(): {}, st.branches[6].ID(): {}, } actualClosestConflictAncestors := map[BranchID]struct{}{} aggregatedBranchConflictParents.Consume(func(branch *Branch) { actualClosestConflictAncestors[branch.ID()] = struct{}{} }) assert.Equal(t, expectedClosestConflictAncestors, actualClosestConflictAncestors) } { aggregatedBranchConflictParents := make(CachedBranches) err := branchManager.collectClosestConflictAncestors(st.branches[13], aggregatedBranchConflictParents) assert.NoError(t, err) expectedClosestConflictAncestors := map[BranchID]struct{}{ st.branches[3].ID(): {}, st.branches[6].ID(): {}, } actualClosestConflictAncestors := map[BranchID]struct{}{} aggregatedBranchConflictParents.Consume(func(branch *Branch) { actualClosestConflictAncestors[branch.ID()] = struct{}{} }) assert.Equal(t, expectedClosestConflictAncestors, actualClosestConflictAncestors) } { aggregatedBranchConflictParents := make(CachedBranches) err := branchManager.collectClosestConflictAncestors(st.branches[14], aggregatedBranchConflictParents) assert.NoError(t, err) expectedClosestConflictAncestors := map[BranchID]struct{}{ st.branches[8].ID(): {}, st.branches[6].ID(): {}, } actualClosestConflictAncestors := map[BranchID]struct{}{} aggregatedBranchConflictParents.Consume(func(branch *Branch) { actualClosestConflictAncestors[branch.ID()] = struct{}{} }) assert.Equal(t, expectedClosestConflictAncestors, actualClosestConflictAncestors) } { aggregatedBranchConflictParents := make(CachedBranches) err := branchManager.collectClosestConflictAncestors(st.branches[16], aggregatedBranchConflictParents) assert.NoError(t, err) expectedClosestConflictAncestors := map[BranchID]struct{}{ st.branches[13].ID(): {}, st.branches[14].ID(): {}, } actualClosestConflictAncestors := map[BranchID]struct{}{} aggregatedBranchConflictParents.Consume(func(branch *Branch) { actualClosestConflictAncestors[branch.ID()] = struct{}{} }) assert.Equal(t, expectedClosestConflictAncestors, actualClosestConflictAncestors) } { // lets check whether an aggregated branch out of branch 2 and aggr. branch 11 // resolves to the same ID as when the actual parents (6 and 8) of aggr. branch 11 // are used in conjunction with branch 2. parentsBranches := CachedBranches{ st.branches[2].ID(): st.cachedBranches[2].Retain(), st.branches[6].ID(): st.cachedBranches[6].Retain(), st.branches[8].ID(): st.cachedBranches[8].Retain(), } expectedAggrBranchID := branchManager.generateAggregatedBranchID(parentsBranches) cachedAggrBranch17, err := branchManager.AggregateBranches(st.branches[2].ID(), st.branches[11].ID()) assert.NoError(t, err) assert.Equal(t, expectedAggrBranchID, cachedAggrBranch17.Unwrap().ID()) cachedAggrBranch17.Release() } } func TestBranchManager_ConflictMembers(t *testing.T) { branchManager := New(mapdb.NewMapDB()) cachedBranch2, _ := branchManager.Fork(BranchID{2}, []BranchID{MasterBranchID}, []ConflictID{{0}}) defer cachedBranch2.Release() branch2 := cachedBranch2.Unwrap() cachedBranch3, _ := branchManager.Fork(BranchID{3}, []BranchID{MasterBranchID}, []ConflictID{{0}}) defer cachedBranch3.Release() branch3 := cachedBranch3.Unwrap() // assert conflict members expectedConflictMembers := map[BranchID]struct{}{ branch2.ID(): {}, branch3.ID(): {}, } actualConflictMembers := map[BranchID]struct{}{} branchManager.ConflictMembers(ConflictID{0}).Consume(func(conflictMember *ConflictMember) { actualConflictMembers[conflictMember.BranchID()] = struct{}{} }) assert.Equal(t, expectedConflictMembers, actualConflictMembers) // add branch 4 cachedBranch4, _ := branchManager.Fork(BranchID{4}, []BranchID{MasterBranchID}, []ConflictID{{0}}) defer cachedBranch4.Release() branch4 := cachedBranch4.Unwrap() // branch 4 should now also be part of the conflict set expectedConflictMembers = map[BranchID]struct{}{ branch2.ID(): {}, branch3.ID(): {}, branch4.ID(): {}, } actualConflictMembers = map[BranchID]struct{}{} branchManager.ConflictMembers(ConflictID{0}).Consume(func(conflictMember *ConflictMember) { actualConflictMembers[conflictMember.BranchID()] = struct{}{} }) assert.Equal(t, expectedConflictMembers, actualConflictMembers) } // ./img/testelevation.png func TestBranchManager_ElevateConflictBranch(t *testing.T) { branchManager := New(mapdb.NewMapDB()) cachedBranch2, _ := branchManager.Fork(BranchID{2}, []BranchID{MasterBranchID}, []ConflictID{{0}}) defer cachedBranch2.Release() branch2 := cachedBranch2.Unwrap() cachedBranch3, _ := branchManager.Fork(BranchID{3}, []BranchID{MasterBranchID}, []ConflictID{{0}}) defer cachedBranch3.Release() branch3 := cachedBranch3.Unwrap() cachedBranch4, _ := branchManager.Fork(BranchID{4}, []BranchID{branch2.ID()}, []ConflictID{{1}}) defer cachedBranch4.Release() branch4 := cachedBranch4.Unwrap() cachedBranch5, _ := branchManager.Fork(BranchID{5}, []BranchID{branch2.ID()}, []ConflictID{{1}}) defer cachedBranch5.Release() branch5 := cachedBranch5.Unwrap() cachedBranch6, _ := branchManager.Fork(BranchID{6}, []BranchID{branch4.ID()}, []ConflictID{{2}}) defer cachedBranch6.Release() branch6 := cachedBranch6.Unwrap() cachedBranch7, _ := branchManager.Fork(BranchID{7}, []BranchID{branch4.ID()}, []ConflictID{{2}}) defer cachedBranch7.Release() branch7 := cachedBranch7.Unwrap() // lets assume the conflict between 2 and 3 is resolved and therefore we elevate // branches 4 and 5 to the master branch isConflictBranch, modified, err := branchManager.ElevateConflictBranch(branch4.ID(), MasterBranchID) assert.NoError(t, err, "branch 4 should have been elevated to the master branch") assert.True(t, isConflictBranch, "branch 4 should have been a conflict branch") assert.True(t, modified, "branch 4 should have been modified") assert.Equal(t, MasterBranchID, branch4.ParentBranches()[0], "branch 4's parent should now be the master branch") isConflictBranch, modified, err = branchManager.ElevateConflictBranch(branch5.ID(), MasterBranchID) assert.NoError(t, err, "branch 5 should have been elevated to the master branch") assert.True(t, isConflictBranch, "branch 5 should have been a conflict branch") assert.True(t, modified, "branch 5 should have been modified") assert.Equal(t, MasterBranchID, branch5.ParentBranches()[0], "branch 5's parent should now be the master branch") // check whether the child branches are what we expect them to be of the master branch expectedMasterChildBranches := map[BranchID]struct{}{ branch2.ID(): {}, branch3.ID(): {}, branch4.ID(): {}, branch5.ID(): {}, } actualMasterChildBranches := map[BranchID]struct{}{} branchManager.ChildBranches(MasterBranchID).Consume(func(childBranch *ChildBranch) { actualMasterChildBranches[childBranch.ChildID()] = struct{}{} }) assert.Equal(t, expectedMasterChildBranches, actualMasterChildBranches) // check that 4 and 5 no longer are children of branch 2 expectedBranch2ChildBranches := map[BranchID]struct{}{} actualBranch2ChildBranches := map[BranchID]struct{}{} branchManager.ChildBranches(branch2.ID()).Consume(func(childBranch *ChildBranch) { actualBranch2ChildBranches[childBranch.ChildID()] = struct{}{} }) assert.Equal(t, expectedBranch2ChildBranches, actualBranch2ChildBranches) // lets assume the conflict between 4 and 5 is resolved and therefore we elevate // branches 6 and 7 to the master branch isConflictBranch, modified, err = branchManager.ElevateConflictBranch(branch6.ID(), MasterBranchID) assert.NoError(t, err, "branch 6 should have been elevated to the master branch") assert.True(t, isConflictBranch, "branch 6 should have been a conflict branch") assert.True(t, modified, "branch 6 should have been modified") assert.Equal(t, MasterBranchID, branch6.ParentBranches()[0], "branch 6's parent should now be the master branch") isConflictBranch, modified, err = branchManager.ElevateConflictBranch(branch7.ID(), MasterBranchID) assert.NoError(t, err, "branch 7 should have been elevated to the master branch") assert.True(t, isConflictBranch, "branch 7 should have been a conflict branch") assert.True(t, modified, "branch 7 should have been modified") assert.Equal(t, MasterBranchID, branch7.ParentBranches()[0], "branch 7's parent should now be the master branch") // check whether the child branches are what we expect them to be of the master branch expectedMasterChildBranches = map[BranchID]struct{}{ branch2.ID(): {}, branch3.ID(): {}, branch4.ID(): {}, branch5.ID(): {}, branch6.ID(): {}, branch7.ID(): {}, } actualMasterChildBranches = map[BranchID]struct{}{} branchManager.ChildBranches(MasterBranchID).Consume(func(childBranch *ChildBranch) { actualMasterChildBranches[childBranch.ChildID()] = struct{}{} }) assert.Equal(t, expectedMasterChildBranches, actualMasterChildBranches) // check that 6 and 7 no longer are children of branch 4 expectedBranch4ChildBranches := map[BranchID]struct{}{} actualBranch4ChildBranches := map[BranchID]struct{}{} branchManager.ChildBranches(branch4.ID()).Consume(func(childBranch *ChildBranch) { actualBranch4ChildBranches[childBranch.ChildID()] = struct{}{} }) assert.Equal(t, expectedBranch4ChildBranches, actualBranch4ChildBranches) // TODO: branches are never deleted? } // ./img/testconflictdetection.png func TestBranchManager_BranchesConflicting(t *testing.T) { branchManager := New(mapdb.NewMapDB()) cachedBranch2, _ := branchManager.Fork(BranchID{2}, []BranchID{MasterBranchID}, []ConflictID{{0}}) defer cachedBranch2.Release() branch2 := cachedBranch2.Unwrap() cachedBranch3, _ := branchManager.Fork(BranchID{3}, []BranchID{MasterBranchID}, []ConflictID{{0}}) defer cachedBranch3.Release() branch3 := cachedBranch3.Unwrap() { conflicting, err := branchManager.BranchesConflicting(MasterBranchID, branch2.ID()) assert.NoError(t, err) assert.False(t, conflicting, "branch 2 should not be conflicting with master branch") conflicting, err = branchManager.BranchesConflicting(MasterBranchID, branch2.ID()) assert.NoError(t, err) assert.False(t, conflicting, "branch 3 should not be conflicting with master branch") conflicting, err = branchManager.BranchesConflicting(branch2.ID(), branch3.ID()) assert.NoError(t, err) assert.True(t, conflicting, "branch 2 & 3 should be conflicting with each other") } // spawn of branch 4 and 5 from branch 2 cachedBranch4, _ := branchManager.Fork(BranchID{4}, []BranchID{branch2.ID()}, []ConflictID{{1}}) defer cachedBranch4.Release() branch4 := cachedBranch4.Unwrap() cachedBranch5, _ := branchManager.Fork(BranchID{5}, []BranchID{branch2.ID()}, []ConflictID{{1}}) defer cachedBranch5.Release() branch5 := cachedBranch5.Unwrap() { conflicting, err := branchManager.BranchesConflicting(MasterBranchID, branch4.ID()) assert.NoError(t, err) assert.False(t, conflicting, "branch 4 should not be conflicting with master branch") conflicting, err = branchManager.BranchesConflicting(branch3.ID(), branch4.ID()) assert.NoError(t, err) assert.True(t, conflicting, "branch 3 & 4 should be conflicting with each other") conflicting, err = branchManager.BranchesConflicting(MasterBranchID, branch5.ID()) assert.NoError(t, err) assert.False(t, conflicting, "branch 5 should not be conflicting with master branch") conflicting, err = branchManager.BranchesConflicting(branch3.ID(), branch5.ID()) assert.NoError(t, err) assert.True(t, conflicting, "branch 3 & 5 should be conflicting with each other") // since both consume the same output conflicting, err = branchManager.BranchesConflicting(branch4.ID(), branch5.ID()) assert.NoError(t, err) assert.True(t, conflicting, "branch 4 & 5 should be conflicting with each other") } // branch 6, 7 are on the same level as 2 and 3 but are not part of that conflict set cachedBranch6, _ := branchManager.Fork(BranchID{6}, []BranchID{MasterBranchID}, []ConflictID{{2}}) defer cachedBranch6.Release() branch6 := cachedBranch6.Unwrap() cachedBranch7, _ := branchManager.Fork(BranchID{7}, []BranchID{MasterBranchID}, []ConflictID{{2}}) defer cachedBranch7.Release() branch7 := cachedBranch7.Unwrap() { conflicting, err := branchManager.BranchesConflicting(branch2.ID(), branch6.ID()) assert.NoError(t, err) assert.False(t, conflicting, "branch 6 should not be conflicting with branch 2") conflicting, err = branchManager.BranchesConflicting(branch3.ID(), branch6.ID()) assert.NoError(t, err) assert.False(t, conflicting, "branch 6 should not be conflicting with branch 3") conflicting, err = branchManager.BranchesConflicting(branch2.ID(), branch7.ID()) assert.NoError(t, err) assert.False(t, conflicting, "branch 7 should not be conflicting with branch 2") conflicting, err = branchManager.BranchesConflicting(branch3.ID(), branch7.ID()) assert.NoError(t, err) assert.False(t, conflicting, "branch 7 should not be conflicting with branch 3") conflicting, err = branchManager.BranchesConflicting(branch6.ID(), branch7.ID()) assert.NoError(t, err) assert.True(t, conflicting, "branch 6 & 7 should be conflicting with each other") conflicting, err = branchManager.BranchesConflicting(branch4.ID(), branch6.ID()) assert.NoError(t, err) assert.False(t, conflicting, "branch 6 should not be conflicting with branch 4") conflicting, err = branchManager.BranchesConflicting(branch5.ID(), branch6.ID()) assert.NoError(t, err) assert.False(t, conflicting, "branch 6 should not be conflicting with branch 5") conflicting, err = branchManager.BranchesConflicting(branch4.ID(), branch7.ID()) assert.NoError(t, err) assert.False(t, conflicting, "branch 7 should not be conflicting with branch 4") conflicting, err = branchManager.BranchesConflicting(branch5.ID(), branch7.ID()) assert.NoError(t, err) assert.False(t, conflicting, "branch 7 should not be conflicting with branch 5") } // aggregated branch out of branch 4 (child of branch 2) and branch 6 cachedAggrBranch8, aggrBranchErr := branchManager.AggregateBranches(branch4.ID(), branch6.ID()) assert.NoError(t, aggrBranchErr) defer cachedAggrBranch8.Release() aggrBranch8 := cachedAggrBranch8.Unwrap() { conflicting, err := branchManager.BranchesConflicting(aggrBranch8.ID(), MasterBranchID) assert.NoError(t, err) assert.False(t, conflicting, "aggr. branch 8 should not be conflicting with master branch") conflicting, err = branchManager.BranchesConflicting(aggrBranch8.ID(), branch2.ID()) assert.NoError(t, err) assert.False(t, conflicting, "aggr. branch 8 should not be conflicting with branch 2") // conflicting since branch 2 and branch 3 are conflicting, err = branchManager.BranchesConflicting(aggrBranch8.ID(), branch3.ID()) assert.NoError(t, err) assert.True(t, conflicting, "aggr. branch 8 & branch 3 should be conflicting with each other") // conflicting since branch 4 and branch 5 are conflicting, err = branchManager.BranchesConflicting(aggrBranch8.ID(), branch5.ID()) assert.NoError(t, err) assert.True(t, conflicting, "aggr. branch 8 & branch 5 should be conflicting with each other") // conflicting since branch 6 and branch 7 are conflicting, err = branchManager.BranchesConflicting(aggrBranch8.ID(), branch7.ID()) assert.NoError(t, err) assert.True(t, conflicting, "aggr. branch 8 & branch 7 should be conflicting with each other") } // aggregated branch out of aggr. branch 8 and branch 7: // should fail since branch 6 & 7 are conflicting _, aggrBranchErr = branchManager.AggregateBranches(aggrBranch8.ID(), branch7.ID()) assert.Error(t, aggrBranchErr, "can't aggregate branches aggr. branch 8 & conflict branch 7") // aggregated branch out of branch 5 (child of branch 2) and branch 7 cachedAggrBranch9, aggrBranchErr := branchManager.AggregateBranches(branch5.ID(), branch7.ID()) assert.NoError(t, aggrBranchErr) defer cachedAggrBranch9.Release() aggrBranch9 := cachedAggrBranch9.Unwrap() assert.NotEqual(t, aggrBranch8.ID().String(), aggrBranch9.ID().String(), "aggr. branches 8 & 9 should have different IDs") { conflicting, err := branchManager.BranchesConflicting(aggrBranch9.ID(), MasterBranchID) assert.NoError(t, err) assert.False(t, conflicting, "aggr. branch 9 should not be conflicting with master branch") // aggr. branch 8 and 9 should be conflicting, since 4 & 5 and 6 & 7 are conflicting, err = branchManager.BranchesConflicting(aggrBranch8.ID(), aggrBranch9.ID()) assert.NoError(t, err) assert.True(t, conflicting, "aggr. branch 8 & branch 9 should be conflicting with each other") // conflicting since branch 3 & 2 are conflicting, err = branchManager.BranchesConflicting(branch3.ID(), aggrBranch9.ID()) assert.NoError(t, err) assert.True(t, conflicting, "aggr. branch 9 & branch 3 should be conflicting with each other") } // aggregated branch out of branch 3 and branch 6 cachedAggrBranch10, aggrBranchErr := branchManager.AggregateBranches(branch3.ID(), branch6.ID()) assert.NoError(t, aggrBranchErr) defer cachedAggrBranch10.Release() aggrBranch10 := cachedAggrBranch10.Unwrap() { conflicting, err := branchManager.BranchesConflicting(aggrBranch10.ID(), MasterBranchID) assert.NoError(t, err) assert.False(t, conflicting, "aggr. branch 10 should not be conflicting with master branch") // aggr. branch 8 and 10 should be conflicting, since 2 & 3 are conflicting, err = branchManager.BranchesConflicting(aggrBranch8.ID(), aggrBranch10.ID()) assert.NoError(t, err) assert.True(t, conflicting, "aggr. branch 8 & branch 10 should be conflicting with each other") // aggr. branch 9 and 10 should be conflicting, since 2 & 3 and 6 & 7 are conflicting, err = branchManager.BranchesConflicting(aggrBranch9.ID(), aggrBranch10.ID()) assert.NoError(t, err) assert.True(t, conflicting, "aggr. branch 9 & branch 10 should be conflicting with each other") } // branch 11, 12 are on the same level as 2 & 3 and 6 & 7 but are not part of either conflict set cachedBranch11, _ := branchManager.Fork(BranchID{11}, []BranchID{MasterBranchID}, []ConflictID{{3}}) defer cachedBranch11.Release() branch11 := cachedBranch11.Unwrap() cachedBranch12, _ := branchManager.Fork(BranchID{12}, []BranchID{MasterBranchID}, []ConflictID{{3}}) defer cachedBranch12.Release() branch12 := cachedBranch12.Unwrap() { conflicting, err := branchManager.BranchesConflicting(MasterBranchID, branch11.ID()) assert.NoError(t, err) assert.False(t, conflicting, "branch 11 should not be conflicting with master branch") conflicting, err = branchManager.BranchesConflicting(MasterBranchID, branch12.ID()) assert.NoError(t, err) assert.False(t, conflicting, "branch 12 should not be conflicting with master branch") conflicting, err = branchManager.BranchesConflicting(branch11.ID(), branch12.ID()) assert.NoError(t, err) assert.True(t, conflicting, "branch 11 & 12 should be conflicting with each other") } // aggr. branch 13 out of branch 6 and 11 cachedAggrBranch13, aggrBranchErr := branchManager.AggregateBranches(branch6.ID(), branch11.ID()) assert.NoError(t, aggrBranchErr) defer cachedAggrBranch13.Release() aggrBranch13 := cachedAggrBranch13.Unwrap() { conflicting, err := branchManager.BranchesConflicting(aggrBranch13.ID(), aggrBranch9.ID()) assert.NoError(t, err) assert.True(t, conflicting, "aggr. branch 9 & 13 should be conflicting with each other") conflicting, err = branchManager.BranchesConflicting(aggrBranch13.ID(), aggrBranch8.ID()) assert.NoError(t, err) assert.False(t, conflicting, "aggr. branch 8 & 13 should not be conflicting with each other") conflicting, err = branchManager.BranchesConflicting(aggrBranch13.ID(), aggrBranch10.ID()) assert.NoError(t, err) assert.False(t, conflicting, "aggr. branch 10 & 13 should not be conflicting with each other") } // aggr. branch 14 out of aggr. branch 10 and 13 cachedAggrBranch14, aggrBranchErr := branchManager.AggregateBranches(aggrBranch10.ID(), aggrBranch13.ID()) assert.NoError(t, aggrBranchErr) defer cachedAggrBranch14.Release() aggrBranch14 := cachedAggrBranch14.Unwrap() { // aggr. branch 9 has parent branch 7 which conflicts with ancestor branch 6 of aggr. branch 14 conflicting, err := branchManager.BranchesConflicting(aggrBranch14.ID(), aggrBranch9.ID()) assert.NoError(t, err) assert.True(t, conflicting, "aggr. branch 14 & 9 should be conflicting with each other") // aggr. branch has ancestor branch 2 which conflicts with ancestor branch 3 of aggr. branch 14 conflicting, err = branchManager.BranchesConflicting(aggrBranch14.ID(), aggrBranch8.ID()) assert.NoError(t, err) assert.True(t, conflicting, "aggr. branch 14 & 8 should be conflicting with each other") } // aggr. branch 15 out of branch 2, 7 and 12 cachedAggrBranch15, aggrBranchErr := branchManager.AggregateBranches(branch2.ID(), branch7.ID(), branch12.ID()) assert.NoError(t, aggrBranchErr) defer cachedAggrBranch15.Release() aggrBranch15 := cachedAggrBranch15.Unwrap() { // aggr. branch 13 has parent branches 11 & 6 which conflicts which conflicts with ancestor branches 12 & 7 of aggr. branch 15 conflicting, err := branchManager.BranchesConflicting(aggrBranch15.ID(), aggrBranch13.ID()) assert.NoError(t, err) assert.True(t, conflicting, "aggr. branch 15 & 13 should be conflicting with each other") // aggr. branch 10 has parent branches 3 & 6 which conflicts with ancestor branches 2 & 7 of aggr. branch 15 conflicting, err = branchManager.BranchesConflicting(aggrBranch15.ID(), aggrBranch10.ID()) assert.NoError(t, err) assert.True(t, conflicting, "aggr. branch 15 & 10 should be conflicting with each other") // aggr. branch 8 has parent branch 6 which conflicts with ancestor branch 7 of aggr. branch 15 conflicting, err = branchManager.BranchesConflicting(aggrBranch15.ID(), aggrBranch8.ID()) assert.NoError(t, err) assert.True(t, conflicting, "aggr. branch 15 & 8 should be conflicting with each other") } // aggr. branch 16 out of aggr. branches 15 and 9 cachedAggrBranch16, aggrBranchErr := branchManager.AggregateBranches(aggrBranch15.ID(), aggrBranch9.ID()) assert.NoError(t, aggrBranchErr) defer cachedAggrBranch16.Release() aggrBranch16 := cachedAggrBranch16.Unwrap() { // sanity check conflicting, err := branchManager.BranchesConflicting(aggrBranch16.ID(), aggrBranch9.ID()) assert.NoError(t, err) assert.False(t, conflicting, "aggr. branch 16 & 9 should not be conflicting with each other") // sanity check conflicting, err = branchManager.BranchesConflicting(aggrBranch16.ID(), branch7.ID()) assert.NoError(t, err) assert.False(t, conflicting, "aggr. branch 16 & 9 should not be conflicting with each other") conflicting, err = branchManager.BranchesConflicting(aggrBranch16.ID(), aggrBranch13.ID()) assert.NoError(t, err) assert.True(t, conflicting, "aggr. branch 16 & 13 should be conflicting with each other") conflicting, err = branchManager.BranchesConflicting(aggrBranch16.ID(), aggrBranch14.ID()) assert.NoError(t, err) assert.True(t, conflicting, "aggr. branch 16 & 14 should be conflicting with each other") conflicting, err = branchManager.BranchesConflicting(aggrBranch16.ID(), aggrBranch8.ID()) assert.NoError(t, err) assert.True(t, conflicting, "aggr. branch 16 & 8 should be conflicting with each other") } } func TestBranchManager_SetBranchPreferred(t *testing.T) { branchManager := New(mapdb.NewMapDB()) event := newEventMock(t, branchManager) defer event.DetachAll() cachedBranch2, _ := branchManager.Fork(BranchID{2}, []BranchID{MasterBranchID}, []ConflictID{{0}}) defer cachedBranch2.Release() branch2 := cachedBranch2.Unwrap() cachedBranch3, _ := branchManager.Fork(BranchID{3}, []BranchID{MasterBranchID}, []ConflictID{{0}}) defer cachedBranch3.Release() branch3 := cachedBranch3.Unwrap() assert.False(t, branch2.Preferred(), "branch 2 should not be preferred") assert.False(t, branch2.Liked(), "branch 2 should not be liked") assert.False(t, branch3.Preferred(), "branch 3 should not be preferred") assert.False(t, branch3.Liked(), "branch 3 should not be liked") cachedBranch4, _ := branchManager.Fork(BranchID{4}, []BranchID{branch2.ID()}, []ConflictID{{1}}) defer cachedBranch4.Release() branch4 := cachedBranch4.Unwrap() cachedBranch5, _ := branchManager.Fork(BranchID{5}, []BranchID{branch2.ID()}, []ConflictID{{1}}) defer cachedBranch5.Release() branch5 := cachedBranch5.Unwrap() // lets assume branch 4 is preferred since its underlying transaction was longer // solid than the avg. network delay before the conflicting transaction which created // the conflict set was received event.Expect("BranchPreferred", branch4) modified, err := branchManager.SetBranchPreferred(branch4.ID(), true) assert.NoError(t, err) assert.True(t, modified) assert.True(t, branch4.Preferred(), "branch 4 should be preferred") // is not liked because its parents aren't liked, respectively branch 2 assert.False(t, branch4.Liked(), "branch 4 should not be liked") assert.False(t, branch5.Preferred(), "branch 5 should not be preferred") assert.False(t, branch5.Liked(), "branch 5 should not be liked") // now branch 2 becomes preferred via FPC, this causes branch 2 to be liked (since // the master branch is liked) and its liked state propagates to branch 4 (but not branch 5) event.Expect("BranchPreferred", branch2) event.Expect("BranchLiked", branch2) event.Expect("BranchLiked", branch4) modified, err = branchManager.SetBranchPreferred(branch2.ID(), true) assert.NoError(t, err) assert.True(t, modified) assert.True(t, branch2.Liked(), "branch 2 should be liked") assert.True(t, branch2.Preferred(), "branch 2 should be preferred") assert.True(t, branch4.Liked(), "branch 4 should be liked") assert.True(t, branch4.Preferred(), "branch 4 should still be preferred") assert.False(t, branch5.Liked(), "branch 5 should not be liked") assert.False(t, branch5.Preferred(), "branch 5 should not be preferred") // now the network decides that branch 5 is preferred (via FPC), thus branch 4 should lose its // preferred and liked state and branch 5 should instead become preferred and liked event.Expect("BranchPreferred", branch5) event.Expect("BranchLiked", branch5) event.Expect("BranchUnpreferred", branch4) event.Expect("BranchDisliked", branch4) modified, err = branchManager.SetBranchPreferred(branch5.ID(), true) assert.NoError(t, err) assert.True(t, modified) // sanity check for branch 2 state assert.True(t, branch2.Liked(), "branch 2 should be liked") assert.True(t, branch2.Preferred(), "branch 2 should be preferred") // check that branch 4 is disliked and not preferred assert.False(t, branch4.Liked(), "branch 4 should be disliked") assert.False(t, branch4.Preferred(), "branch 4 should not be preferred") assert.True(t, branch5.Liked(), "branch 5 should be liked") assert.True(t, branch5.Preferred(), "branch 5 should be preferred") // check that all event have been triggered event.AssertExpectations(t) } func TestBranchManager_SetBranchPreferred2(t *testing.T) { branchManager := New(mapdb.NewMapDB()) event := newEventMock(t, branchManager) defer event.DetachAll() cachedBranch2, _ := branchManager.Fork(BranchID{2}, []BranchID{MasterBranchID}, []ConflictID{{0}}) defer cachedBranch2.Release() branch2 := cachedBranch2.Unwrap() cachedBranch3, _ := branchManager.Fork(BranchID{3}, []BranchID{MasterBranchID}, []ConflictID{{0}}) defer cachedBranch3.Release() branch3 := cachedBranch3.Unwrap() cachedBranch4, _ := branchManager.Fork(BranchID{4}, []BranchID{branch2.ID()}, []ConflictID{{1}}) defer cachedBranch4.Release() branch4 := cachedBranch4.Unwrap() cachedBranch5, _ := branchManager.Fork(BranchID{5}, []BranchID{branch2.ID()}, []ConflictID{{1}}) defer cachedBranch5.Release() branch5 := cachedBranch5.Unwrap() cachedBranch6, _ := branchManager.Fork(BranchID{6}, []BranchID{MasterBranchID}, []ConflictID{{2}}) defer cachedBranch6.Release() branch6 := cachedBranch6.Unwrap() cachedBranch7, _ := branchManager.Fork(BranchID{7}, []BranchID{MasterBranchID}, []ConflictID{{2}}) defer cachedBranch7.Release() branch7 := cachedBranch7.Unwrap() event.Expect("BranchPreferred", branch2) event.Expect("BranchLiked", branch2) event.Expect("BranchPreferred", branch6) event.Expect("BranchLiked", branch6) // assume branch 2 preferred since solid longer than avg. network delay modified, err := branchManager.SetBranchPreferred(branch2.ID(), true) assert.NoError(t, err) assert.True(t, modified) // assume branch 6 preferred since solid longer than avg. network delay modified, err = branchManager.SetBranchPreferred(branch6.ID(), true) assert.NoError(t, err) assert.True(t, modified) { assert.True(t, branch2.Liked(), "branch 2 should be liked") assert.True(t, branch2.Preferred(), "branch 2 should be preferred") assert.False(t, branch3.Liked(), "branch 3 should not be liked") assert.False(t, branch3.Preferred(), "branch 3 should not be preferred") assert.False(t, branch4.Liked(), "branch 4 should not be liked") assert.False(t, branch4.Preferred(), "branch 4 should not be preferred") assert.False(t, branch5.Liked(), "branch 5 should not be liked") assert.False(t, branch5.Preferred(), "branch 5 should not be preferred") assert.True(t, branch6.Liked(), "branch 6 should be liked") assert.True(t, branch6.Preferred(), "branch 6 should be preferred") assert.False(t, branch7.Liked(), "branch 7 should not be liked") assert.False(t, branch7.Preferred(), "branch 7 should not be preferred") } // throw some aggregated branches into the mix cachedAggrBranch8, err := branchManager.AggregateBranches(branch4.ID(), branch6.ID()) assert.NoError(t, err) defer cachedAggrBranch8.Release() aggrBranch8 := cachedAggrBranch8.Unwrap() // should not be preferred because only 6 is is preferred but not 4 assert.False(t, aggrBranch8.Liked(), "aggr. branch 8 should not be liked") assert.False(t, aggrBranch8.Preferred(), "aggr. branch 8 should not be preferred") cachedAggrBranch9, err := branchManager.AggregateBranches(branch5.ID(), branch7.ID()) assert.NoError(t, err) defer cachedAggrBranch9.Release() aggrBranch9 := cachedAggrBranch9.Unwrap() // branch 5 and 7 are neither liked or preferred assert.False(t, aggrBranch9.Liked(), "aggr. branch 9 should not be liked") assert.False(t, aggrBranch9.Preferred(), "aggr. branch 9 should not be preferred") // should not be preferred because only 6 is is preferred but not 3 cachedAggrBranch10, err := branchManager.AggregateBranches(branch3.ID(), branch6.ID()) assert.NoError(t, err) defer cachedAggrBranch10.Release() aggrBranch10 := cachedAggrBranch10.Unwrap() assert.False(t, aggrBranch10.Liked(), "aggr. branch 10 should not be liked") assert.False(t, aggrBranch10.Preferred(), "aggr. branch 10 should not be preferred") // spawn off conflict branch 11 and 12 cachedBranch11, _ := branchManager.Fork(BranchID{11}, []BranchID{aggrBranch8.ID()}, []ConflictID{{3}}) defer cachedBranch11.Release() branch11 := cachedBranch11.Unwrap() assert.False(t, branch11.Liked(), "aggr. branch 11 should not be liked") assert.False(t, branch11.Preferred(), "aggr. branch 11 should not be preferred") cachedBranch12, _ := branchManager.Fork(BranchID{12}, []BranchID{aggrBranch8.ID()}, []ConflictID{{3}}) defer cachedBranch12.Release() branch12 := cachedBranch12.Unwrap() assert.False(t, branch12.Liked(), "aggr. branch 12 should not be liked") assert.False(t, branch12.Preferred(), "aggr. branch 12 should not be preferred") cachedAggrBranch13, err := branchManager.AggregateBranches(branch4.ID(), branch12.ID()) assert.NoError(t, err) defer cachedAggrBranch13.Release() aggrBranch13 := cachedAggrBranch13.Unwrap() assert.False(t, aggrBranch13.Liked(), "aggr. branch 13 should not be liked") assert.False(t, aggrBranch13.Preferred(), "aggr. branch 13 should not be preferred") // now lets assume FPC finalized on branch 2, 6 and 4 to be preferred. // branches 2 and 6 are already preferred but 4 is newly preferred. Branch 4 therefore // should also become liked, since branch 2 of which it spawns off is liked too. // simulate branch 3 being not preferred from FPC vote // this does not trigger any events as branch 3 was never preferred modified, err = branchManager.SetBranchPreferred(branch3.ID(), false) assert.NoError(t, err) assert.False(t, modified) // simulate branch 7 being not preferred from FPC vote // this does not trigger any events as branch 7 was never preferred modified, err = branchManager.SetBranchPreferred(branch7.ID(), false) assert.NoError(t, err) assert.False(t, modified) event.Expect("BranchPreferred", branch4) event.Expect("BranchLiked", branch4) event.Expect("BranchPreferred", aggrBranch8) event.Expect("BranchLiked", aggrBranch8) // simulate branch 4 being preferred by FPC vote modified, err = branchManager.SetBranchPreferred(branch4.ID(), true) assert.NoError(t, err) assert.True(t, modified) assert.True(t, branch4.Liked(), "branch 4 should be liked") assert.True(t, branch4.Preferred(), "branch 4 should be preferred") // this should cause aggr. branch 8 to also be preferred and liked, since branch 6 and 4 // of which it spawns off are. assert.True(t, aggrBranch8.Liked(), "aggr. branch 8 should be liked") assert.True(t, aggrBranch8.Preferred(), "aggr. branch 8 should be preferred") // check that all event have been triggered event.AssertExpectations(t) }