github.com/btwiuse/jiri@v0.0.0-20191125065820-53353bcfef54/simplemr/mr_test.go (about) 1 // Copyright 2015 The Vanadium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package simplemr_test 6 7 import ( 8 "errors" 9 "fmt" 10 "math/rand" 11 "runtime" 12 "strings" 13 "testing" 14 "time" 15 16 "github.com/btwiuse/jiri/simplemr" 17 ) 18 19 func newChans(chanSize int) (chan *simplemr.Record, chan *simplemr.Record) { 20 return make(chan *simplemr.Record, chanSize), make(chan *simplemr.Record, chanSize) 21 } 22 23 type termCount struct{} 24 25 func (tc *termCount) Map(mr *simplemr.MR, key string, val interface{}) error { 26 text, ok := val.(string) 27 if !ok { 28 return fmt.Errorf("%T is the wrong type", val) 29 } 30 for _, token := range strings.Split(text, " ") { 31 mr.MapOut(token, 1) 32 } 33 return nil 34 } 35 36 func (tc *termCount) Reduce(mr *simplemr.MR, key string, values []interface{}) error { 37 count := 0 38 for _, val := range values { 39 c, ok := val.(int) 40 if !ok { 41 return fmt.Errorf("%T is the wrong type", val) 42 } 43 count += c 44 } 45 mr.ReduceOut(key, count) 46 return nil 47 } 48 49 var ( 50 d1 = "a b c" 51 d2 = "a b c d" 52 d3 = "e f" 53 ) 54 55 func expect(t *testing.T, out chan *simplemr.Record, key string, vals ...int) { 56 rec := <-out 57 if got, want := rec.Key, key; got != want { 58 t.Errorf("got %v, want %v", got, want) 59 } 60 if got, want := len(rec.Values), len(vals); got != want { 61 t.Errorf("got %v, want %v", got, want) 62 return 63 } 64 for i, v := range vals { 65 if got, want := rec.Values[i], v; got != want { 66 t.Errorf("%d: got %v, want %v", i, got, want) 67 } 68 } 69 } 70 71 func TestMR(t *testing.T) { 72 mrt := &simplemr.MR{} 73 in, out := newChans(10) 74 tc := &termCount{} 75 genInput := func() { 76 in <- &simplemr.Record{"d1", []interface{}{d1, d2, d3}} 77 in <- &simplemr.Record{"d2", []interface{}{d1, d2, d3}} 78 close(in) 79 } 80 go genInput() 81 if err := mrt.Run(in, out, tc, tc); err != nil { 82 t.Fatal(err) 83 } 84 85 expect(t, out, "a", 4) 86 expect(t, out, "b", 4) 87 expect(t, out, "c", 4) 88 expect(t, out, "d", 2) 89 expect(t, out, "e", 2) 90 expect(t, out, "f", 2) 91 kvs := <-out 92 if kvs != nil { 93 t.Fatal("expected the channel to be closed") 94 } 95 } 96 97 type slowReducer struct{} 98 99 func (sr *slowReducer) Reduce(mr *simplemr.MR, key string, values []interface{}) error { 100 time.Sleep(time.Hour) 101 return nil 102 } 103 104 func TestTimeout(t *testing.T) { 105 in, out := newChans(1) 106 mrt := &simplemr.MR{Timeout: 100 * time.Millisecond} 107 identity := &simplemr.Identity{} 108 mrt.Run(in, out, identity, identity) 109 if err := mrt.Error(); err == nil || !strings.Contains(err.Error(), "timed out mappers") { 110 t.Fatalf("missing or wrong error: %v", err) 111 } 112 mrt = &simplemr.MR{Timeout: 100 * time.Millisecond} 113 in, out = newChans(1) 114 in <- &simplemr.Record{"key", []interface{}{"value"}} 115 close(in) 116 mrt.Run(in, out, identity, &slowReducer{}) 117 if err := mrt.Error(); err == nil || !strings.Contains(err.Error(), "timed out reducers") { 118 t.Fatalf("missing or wrong error: %v", err) 119 } 120 } 121 122 type sleeper struct{} 123 124 const sleepTime = time.Millisecond * 100 125 126 func (sl *sleeper) Map(mr *simplemr.MR, key string, val interface{}) error { 127 time.Sleep(sleepTime) 128 mr.MapOut(key, val) 129 return nil 130 } 131 132 func (sl *sleeper) Reduce(mr *simplemr.MR, key string, values []interface{}) error { 133 mr.ReduceOut(key, values...) 134 return nil 135 } 136 137 func runMappers(t *testing.T, bufsize, numMappers int) time.Duration { 138 mrt := &simplemr.MR{NumMappers: numMappers} 139 in, out := newChans(bufsize) 140 sl := &sleeper{} 141 go func() { 142 for i := 0; i < bufsize; i++ { 143 in <- &simplemr.Record{Key: fmt.Sprintf("%d", i), Values: []interface{}{i}} 144 } 145 close(in) 146 }() 147 then := time.Now() 148 if err := mrt.Run(in, out, sl, sl); err != nil { 149 t.Fatal(err) 150 } 151 return time.Since(then) 152 } 153 154 func TestOneMappers(t *testing.T) { 155 bufsize := 5 156 runtime := runMappers(t, bufsize, 1) 157 if got, want := runtime, time.Duration(int64(sleepTime)*int64(bufsize)); got < want { 158 t.Errorf("took %s which is too fast, should be at least %s", got, want) 159 } 160 } 161 162 func TestMultipleMappers(t *testing.T) { 163 numCPUs := runtime.NumCPU() 164 if numCPUs == 1 { 165 t.Skip("can't test concurrency with only one CPU") 166 } 167 bufsize := 5 168 runtime := runMappers(t, bufsize, numCPUs) 169 if got, want := runtime, time.Duration(int64(sleepTime)*int64(bufsize)); got > want { 170 t.Errorf("took %s which is too slow, should take no longer than %s", got, want) 171 } 172 } 173 174 type adder struct{} 175 176 func (a *adder) Map(mr *simplemr.MR, key string, val interface{}) error { 177 i := val.(int) 178 i++ 179 mr.MapOut(key, i) 180 return nil 181 } 182 183 func (a *adder) Reduce(mr *simplemr.MR, key string, values []interface{}) error { 184 mr.ReduceOut(key, values...) 185 return nil 186 } 187 188 func TestChainedMR(t *testing.T) { 189 chanSize := 5 190 in, middle, out := make(chan *simplemr.Record, chanSize), make(chan *simplemr.Record, chanSize), make(chan *simplemr.Record, chanSize) 191 mrt1 := &simplemr.MR{} 192 mrt2 := &simplemr.MR{} 193 adder := &adder{} 194 go mrt1.Run(in, middle, adder, adder) 195 go mrt2.Run(middle, out, adder, adder) 196 in <- &simplemr.Record{"1", []interface{}{1}} 197 in <- &simplemr.Record{"2", []interface{}{2}} 198 close(in) 199 expect(t, out, "1", 3) 200 expect(t, out, "2", 4) 201 if err := mrt1.Error(); err != nil { 202 t.Fatal(err) 203 } 204 if err := mrt2.Error(); err != nil { 205 t.Fatal(err) 206 } 207 } 208 209 type cancelMR struct{ cancelMapper bool } 210 211 var ( 212 errMapperCancelled = errors.New("mapper cancelled") 213 errReducerCancelled = errors.New("reducer cancelled") 214 ) 215 216 func cancelEg(mr *simplemr.MR) error { 217 delay := rand.Int63n(1000) * int64(time.Millisecond) 218 select { 219 case <-mr.CancelCh(): 220 return nil 221 case <-time.After(time.Duration(delay)): 222 mr.Cancel() 223 return nil 224 case <-time.After(time.Hour): 225 } 226 return fmt.Errorf("timeout") 227 } 228 229 func (c *cancelMR) Map(mr *simplemr.MR, key string, val interface{}) error { 230 if c.cancelMapper { 231 return cancelEg(mr) 232 } 233 mr.MapOut(key, val) 234 return nil 235 } 236 237 func (c *cancelMR) Reduce(mr *simplemr.MR, key string, values []interface{}) error { 238 if !c.cancelMapper { 239 return cancelEg(mr) 240 } 241 panic("should never get here") 242 return nil 243 } 244 245 func testCancel(t *testing.T, mapper bool) { 246 mrt := &simplemr.MR{} 247 in, out := newChans(10) 248 cancel := &cancelMR{true} 249 genInput := func() { 250 in <- &simplemr.Record{"d1", []interface{}{d1, d2, d3}} 251 in <- &simplemr.Record{"d2", []interface{}{d1, d2, d3}} 252 close(in) 253 } 254 go genInput() 255 if got, want := mrt.Run(in, out, cancel, cancel), simplemr.ErrMRCancelled; got != want { 256 t.Fatalf("got %v, want %v", got, want) 257 } 258 } 259 260 func TestCancelMappers(t *testing.T) { 261 testCancel(t, true) 262 } 263 264 func TestCancelReducers(t *testing.T) { 265 testCancel(t, false) 266 }