github.com/abhinav/git-pr@v0.6.1-0.20171029234004-54218d68c11b/pr/walk_test.go (about) 1 package pr_test 2 3 import ( 4 "errors" 5 "fmt" 6 "testing" 7 8 "github.com/abhinav/git-pr/pr" 9 "github.com/abhinav/git-pr/pr/prtest" 10 11 "github.com/golang/mock/gomock" 12 "github.com/google/go-github/github" 13 "github.com/stretchr/testify/assert" 14 ) 15 16 type mockChildren struct { 17 ctrl *gomock.Controller 18 } 19 20 func newMockChildren(ctrl *gomock.Controller) *mockChildren { 21 return &mockChildren{ctrl: ctrl} 22 } 23 24 func (m *mockChildren) Expect(pr interface{}) *gomock.Call { 25 return m.ctrl.RecordCall(m, "Call", pr) 26 } 27 28 func (m *mockChildren) Call(pr *github.PullRequest) (out []*github.PullRequest, err error) { 29 results := m.ctrl.Call(m, "Call", pr) 30 out, _ = results[0].([]*github.PullRequest) 31 err, _ = results[1].(error) 32 return 33 } 34 35 func TestWalk(t *testing.T) { 36 type children map[int][]int 37 38 type visit struct { 39 // Whether the children of this node should be visited. 40 VisitChildren bool 41 42 // If non-nil, this visit will fail with an error. 43 Error error 44 45 // If non-nil, this visit will panic with the given value. 46 Panic interface{} 47 } 48 49 type visits map[int]visit 50 51 concurrency := []int{0, 1, 2, 4, 8} 52 53 tests := []struct { 54 Desc string 55 Pulls []int 56 57 Children children 58 Visits map[int]visit 59 60 WantErr []string 61 }{ 62 {Desc: "empty", Pulls: []int{}}, 63 { 64 Desc: "single", 65 Pulls: []int{1}, 66 Children: children{1: {}}, 67 Visits: visits{1: {VisitChildren: true}}, 68 }, 69 { 70 Desc: "single error", 71 Pulls: []int{1}, 72 Visits: visits{1: {Error: errors.New("great sadness")}}, 73 WantErr: []string{"great sadness"}, 74 }, 75 { 76 Desc: "single panic error", 77 Pulls: []int{1}, 78 Visits: visits{ 79 1: {Panic: errors.New("great sadness")}, 80 }, 81 WantErr: []string{"great sadness"}, 82 }, 83 { 84 Desc: "single panic", 85 Pulls: []int{1}, 86 Visits: visits{ 87 1: {Panic: "great sadness"}, 88 }, 89 WantErr: []string{"panic: great sadness"}, 90 }, 91 { 92 Desc: "single no children", 93 Pulls: []int{1}, 94 Visits: visits{1: {}}, 95 }, 96 { 97 Desc: "single hop", 98 Pulls: []int{1, 2, 3}, 99 Children: children{ 100 1: {4, 5}, 101 2: {}, 102 4: {}, 103 5: {}, 104 }, 105 Visits: visits{ 106 1: {VisitChildren: true}, 107 2: {VisitChildren: true}, 108 3: {}, 109 4: {VisitChildren: true}, 110 5: {VisitChildren: true}, 111 }, 112 }, 113 { 114 Desc: "multi hop", 115 Pulls: []int{1, 2}, 116 Children: children{ 117 1: {3}, 118 2: {4, 5}, 119 3: {}, 120 4: {6}, 121 5: {7}, 122 6: {8, 9}, 123 7: {}, 124 8: {10}, 125 9: {}, 126 10: {11}, 127 11: {}, 128 }, 129 Visits: visits{ 130 1: {VisitChildren: true}, 131 2: {VisitChildren: true}, 132 3: {VisitChildren: true}, 133 4: {VisitChildren: true}, 134 5: {VisitChildren: true}, 135 6: {VisitChildren: true}, 136 7: {VisitChildren: true}, 137 8: {VisitChildren: true}, 138 9: {VisitChildren: true}, 139 10: {VisitChildren: true}, 140 11: {VisitChildren: true}, 141 }, 142 }, 143 { 144 Desc: "multi hop errors", 145 Pulls: []int{1, 2}, 146 Children: children{ 147 1: {3}, 148 2: {4, 5}, 149 4: {6}, 150 5: {7}, 151 7: {}, 152 }, 153 Visits: visits{ 154 1: {VisitChildren: true}, 155 2: {VisitChildren: true}, 156 3: {VisitChildren: true, Error: errors.New("something went wrong")}, 157 4: {VisitChildren: true}, 158 5: {VisitChildren: true}, 159 6: {VisitChildren: true, Error: errors.New("great sadness")}, 160 7: {VisitChildren: true}, 161 }, 162 WantErr: []string{"great sadness", "something went wrong"}, 163 }, 164 { 165 Desc: "channel overflow", 166 Pulls: []int{1, 21}, 167 Children: children{ 168 1: {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}, 169 2: {}, 3: {}, 4: {}, 5: {}, 6: {}, 7: {}, 8: {}, 9: {}, 10: {}, 170 11: {}, 12: {}, 13: {}, 14: {}, 15: {}, 16: {}, 17: {}, 18: {}, 19: {}, 20: {}, 171 21: {22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40}, 172 22: {}, 23: {}, 24: {}, 25: {}, 26: {}, 27: {}, 28: {}, 29: {}, 30: {}, 173 31: {}, 32: {}, 33: {}, 34: {}, 35: {}, 36: {}, 37: {}, 38: {}, 39: {}, 40: {}, 174 }, 175 Visits: visits{ 176 1: {VisitChildren: true}, 177 2: {VisitChildren: true}, 178 3: {VisitChildren: true}, 179 4: {VisitChildren: true}, 180 5: {VisitChildren: true}, 181 6: {VisitChildren: true}, 182 7: {VisitChildren: true}, 183 8: {VisitChildren: true}, 184 9: {VisitChildren: true}, 185 10: {VisitChildren: true}, 186 11: {VisitChildren: true}, 187 12: {VisitChildren: true}, 188 13: {VisitChildren: true}, 189 14: {VisitChildren: true}, 190 15: {VisitChildren: true}, 191 16: {VisitChildren: true}, 192 17: {VisitChildren: true}, 193 18: {VisitChildren: true}, 194 19: {VisitChildren: true}, 195 20: {VisitChildren: true}, 196 21: {VisitChildren: true}, 197 22: {VisitChildren: true}, 198 23: {VisitChildren: true}, 199 24: {VisitChildren: true}, 200 25: {VisitChildren: true}, 201 26: {VisitChildren: true}, 202 27: {VisitChildren: true}, 203 28: {VisitChildren: true}, 204 29: {VisitChildren: true}, 205 30: {VisitChildren: true}, 206 31: {VisitChildren: true}, 207 32: {VisitChildren: true}, 208 33: {VisitChildren: true}, 209 34: {VisitChildren: true}, 210 35: {VisitChildren: true}, 211 36: {VisitChildren: true}, 212 37: {VisitChildren: true}, 213 38: {VisitChildren: true}, 214 39: {VisitChildren: true}, 215 40: {VisitChildren: true}, 216 }, 217 }, 218 } 219 220 for _, conc := range concurrency { 221 for _, tt := range tests { 222 name := fmt.Sprintf("concurrency=%v/%v", conc, tt.Desc) 223 t.Run(name, func(t *testing.T) { 224 ctrl := gomock.NewController(t) 225 defer ctrl.Finish() 226 227 getChildren := newMockChildren(ctrl) 228 for parent, children := range tt.Children { 229 getChildren. 230 Expect(prMatcher{Number: parent}). 231 Return(fakePullRequests(children), nil) 232 } 233 234 visitor := prtest.NewMockVisitor(ctrl) 235 for num, visit := range tt.Visits { 236 var v pr.Visitor 237 if visit.VisitChildren { 238 v = visitor 239 } 240 241 call := visitor.EXPECT().Visit(prMatcher{Number: num}) 242 switch { 243 case visit.Error != nil: 244 call.Return(v, visit.Error) 245 case visit.Panic != nil: 246 p := visit.Panic 247 call.Do(func(*github.PullRequest) { panic(p) }).Return(v, nil) 248 default: 249 call.Return(v, nil) 250 } 251 } 252 253 cfg := pr.WalkConfig{ 254 Children: getChildren.Call, 255 Concurrency: conc, 256 } 257 err := pr.Walk(cfg, fakePullRequests(tt.Pulls), visitor) 258 259 if len(tt.WantErr) > 0 { 260 if !assert.Error(t, err) { 261 return 262 } 263 264 for _, msg := range tt.WantErr { 265 assert.Contains(t, err.Error(), msg) 266 } 267 return 268 } 269 270 assert.NoError(t, err) 271 }) 272 } 273 } 274 } 275 276 type prMatcher struct { 277 Number int 278 } 279 280 var _ gomock.Matcher = prMatcher{} 281 282 func (m prMatcher) String() string { 283 return fmt.Sprintf("pull request #%v", m.Number) 284 } 285 286 func (m prMatcher) Matches(x interface{}) bool { 287 pr, ok := x.(*github.PullRequest) 288 if !ok { 289 return false 290 } 291 292 return pr.GetNumber() == m.Number 293 } 294 295 func fakePullRequests(nums []int) []*github.PullRequest { 296 prs := make([]*github.PullRequest, len(nums)) 297 for i, n := range nums { 298 prs[i] = &github.PullRequest{Number: github.Int(n)} 299 } 300 return prs 301 }