package types import ( "fmt" "strings" "testing" ) func genFakeTree() *Thread { tree := &Thread{ Uid: 0, } var prevChild *Thread for i := 1; i < 3; i++ { child := &Thread{ Uid: uint32(i * 10), Parent: tree, PrevSibling: prevChild, } if prevChild != nil { prevChild.NextSibling = child } else if tree.FirstChild == nil { tree.FirstChild = child } else { panic("unreachable") } prevChild = child var prevSecond *Thread for j := 1; j < 3; j++ { second := &Thread{ Uid: child.Uid + uint32(j), Parent: child, PrevSibling: prevSecond, } if prevSecond != nil { prevSecond.NextSibling = second } else if child.FirstChild == nil { child.FirstChild = second } else { panic("unreachable") } prevSecond = second var prevThird *Thread limit := 3 if j == 2 { limit = 8 } for k := 1; k < limit; k++ { third := &Thread{ Uid: second.Uid*10 + uint32(k), Parent: second, PrevSibling: prevThird, } if prevThird != nil { prevThird.NextSibling = third } else if second.FirstChild == nil { second.FirstChild = third } else { panic("unreachable") } prevThird = third } } } return tree } func TestNewWalk(t *testing.T) { tree := genFakeTree() var prefix []string lastLevel := 0 tree.Walk(func(t *Thread, lvl int, e error) error { if e != nil { fmt.Printf("ERROR: %v\n", e) } if lvl > lastLevel && lvl > 1 { // we actually just descended... so figure out what connector we need // level 1 is flush to the root, so we avoid the indentation there if t.Parent.NextSibling != nil { prefix = append(prefix, "│ ") } else { prefix = append(prefix, " ") } } else if lvl < lastLevel { // ascended, need to trim the prefix layers diff := lastLevel - lvl prefix = prefix[:len(prefix)-diff] } var arrow string if t.Parent != nil { if t.NextSibling != nil { arrow = "├─>" } else { arrow = "└─>" } } // format fmt.Printf("%s%s%s\n", strings.Join(prefix, ""), arrow, t) lastLevel = lvl return nil }) } func uidSeq(tree *Thread) string { var seq []string tree.Walk(func(t *Thread, _ int, _ error) error { seq = append(seq, fmt.Sprintf("%d", t.Uid)) return nil }) return strings.Join(seq, ".") } func TestThread_AddChild(t *testing.T) { tests := []struct { name string seq []int want string }{ { name: "ascending", seq: []int{1, 2, 3, 4, 5, 6}, want: "0.1.2.3.4.5.6", }, { name: "descending", seq: []int{6, 5, 4, 3, 2, 1}, want: "0.6.5.4.3.2.1", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { tree := &Thread{Uid: 0} for _, i := range test.seq { tree.AddChild(&Thread{Uid: uint32(i)}) } if got := uidSeq(tree); got != test.want { t.Errorf("got: %s, but wanted: %s", got, test.want) } }) } } func TestThread_OrderedInsert(t *testing.T) { tests := []struct { name string seq []int want string }{ { name: "ascending", seq: []int{1, 2, 3, 4, 5, 6}, want: "0.1.2.3.4.5.6", }, { name: "descending", seq: []int{6, 5, 4, 3, 2, 1}, want: "0.1.2.3.4.5.6", }, { name: "mixed", seq: []int{2, 1, 6, 3, 4, 5}, want: "0.1.2.3.4.5.6", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { tree := &Thread{Uid: 0} for _, i := range test.seq { tree.OrderedInsert(&Thread{Uid: uint32(i)}) } if got := uidSeq(tree); got != test.want { t.Errorf("got: %s, but wanted: %s", got, test.want) } }) } } func TestThread_InsertCmd(t *testing.T) { tests := []struct { name string seq []int want string }{ { name: "ascending", seq: []int{1, 2, 3, 4, 5, 6}, want: "0.6.4.2.1.3.5", }, { name: "descending", seq: []int{6, 5, 4, 3, 2, 1}, want: "0.6.4.2.1.3.5", }, { name: "mixed", seq: []int{2, 1, 6, 3, 4, 5}, want: "0.6.4.2.1.3.5", }, } sortMap := map[uint32]int{ uint32(6): 1, uint32(4): 2, uint32(2): 3, uint32(1): 4, uint32(3): 5, uint32(5): 6, } // bigger compares the new child with the next node and returns true if // the child node is bigger and false otherwise. bigger := func(newNode, nextChild *Thread) bool { return sortMap[newNode.Uid] > sortMap[nextChild.Uid] } for _, test := range tests { t.Run(test.name, func(t *testing.T) { tree := &Thread{Uid: 0} for _, i := range test.seq { tree.InsertCmp(&Thread{Uid: uint32(i)}, bigger) } if got := uidSeq(tree); got != test.want { t.Errorf("got: %s, but wanted: %s", got, test.want) } }) } }