github.com/puellanivis/breton@v0.2.16/lib/mapreduce/mapreduce_test.go (about) 1 package mapreduce 2 3 import ( 4 "context" 5 "runtime" 6 "sort" 7 "strings" 8 "sync" 9 "testing" 10 ) 11 12 type StringCollector struct { 13 a [][]string 14 } 15 16 func (sc *StringCollector) Reduce(ctx context.Context, in interface{}) error { 17 a := in.([]string) 18 19 sc.a = append(sc.a, a) 20 21 return nil 22 } 23 24 func (sc *StringCollector) reset() { 25 sc.a = nil 26 } 27 28 var ( 29 stringReceiver = MapFunc(func(ctx context.Context, in interface{}) (out interface{}, err error) { 30 var r []string 31 32 for _, s := range in.([]string) { 33 r = append(r, s) 34 runtime.Gosched() 35 } 36 37 return r, nil 38 }) 39 40 chanReceiver = MapFunc(func(ctx context.Context, in interface{}) (out interface{}, err error) { 41 var r []string 42 43 for s := range in.(<-chan string) { 44 r = append(r, s) 45 runtime.Gosched() 46 } 47 48 return r, nil 49 }) 50 ) 51 52 var ( 53 testString = "abcdefghijklmnopqrstuvwxyz" 54 testInput = strings.Split(testString, "") 55 ) 56 57 func TestMapReduceOverSlice(t *testing.T) { 58 DefaultThreadCount = -1 59 60 sc := &StringCollector{} 61 mr := New(stringReceiver, sc, WithThreadCount(1)) 62 ctx := context.Background() 63 64 f := func(n int) { 65 sc.reset() 66 67 for err := range mr.Run(ctx, testInput, WithThreadCount(n), WithMapperCount(n), WithOrdering(false)) { 68 t.Errorf("%d mappers: %+v", n, err) 69 } 70 71 if n > 0 && len(sc.a) != n { 72 t.Log(n, sc.a) 73 t.Errorf("wrong number of mappers ran, expected %d, but got %d", n, len(sc.a)) 74 } 75 76 var r []rune 77 for _, v := range sc.a { 78 for _, s := range v { 79 r = append(r, []rune(s)...) 80 } 81 } 82 83 sort.Slice(r, func(i, j int) bool { return r[i] < r[j] }) 84 got := string(r) 85 86 if got != testString { 87 t.Logf("mapreduce([]string, %d): %q", n, got) 88 t.Errorf("mapreduce over map with %d mappers did not process all elements, expected %q got %q ", n, testString, got) 89 } 90 } 91 92 for i := -1; i <= len(testInput)+1; i++ { 93 f(i) 94 } 95 } 96 97 func TestOrderedMapReduceOverSlice(t *testing.T) { 98 DefaultThreadCount = -1 99 maxN := len(testInput) 100 101 var wg sync.WaitGroup 102 103 unorderedStringReceiver := MapFunc(func(ctx context.Context, in interface{}) (out interface{}, err error) { 104 var r []string 105 106 flag := true 107 108 for _, s := range in.([]string) { 109 r = append(r, s) 110 111 if s == testInput[0] { 112 wg.Wait() 113 flag = false 114 } 115 } 116 117 if flag { 118 wg.Done() 119 } 120 121 return r, nil 122 }) 123 124 sc := &StringCollector{} 125 mr := New(unorderedStringReceiver, sc, WithThreadCount(1), WithOrdering(true)) 126 ctx := context.Background() 127 128 // the WithOrdering(false) here should override the default WithOrder(true) set on the mapreduce.New() 129 wg.Add(maxN - 1) 130 for err := range mr.Run(ctx, testInput, WithThreadCount(maxN), WithMapperCount(maxN), WithOrdering(false)) { 131 t.Errorf("%d mappers: %+v", maxN, err) 132 } 133 134 t.Log("ordering=false", maxN, sc.a) 135 136 var r []rune 137 for _, v := range sc.a { 138 for _, s := range v { 139 r = append(r, []rune(s)...) 140 } 141 } 142 143 if string(r) == testString { 144 t.Fatalf("testing relies upon runtime.Gosched() producing a non-ordered slice collection.") 145 } 146 147 sc.a = nil 148 149 wg.Add(maxN - 1) 150 for err := range mr.Run(ctx, testInput, WithThreadCount(maxN), WithMapperCount(maxN)) { 151 t.Errorf("%d mappers: %+v", maxN, err) 152 } 153 154 t.Log("ordering=true", maxN, sc.a) 155 156 r = nil 157 for _, v := range sc.a { 158 for _, s := range v { 159 r = append(r, []rune(s)...) 160 } 161 } 162 163 got := string(r) 164 if got != testString { 165 t.Fatalf("an ordered MapReduce should have returned an ordered slice collection, expected %q, got %q", testString, got) 166 } 167 } 168 169 func TestMapReduceOverMap(t *testing.T) { 170 DefaultThreadCount = -1 171 172 ctx, cancel := context.WithCancel(context.Background()) 173 defer cancel() 174 175 sc := new(StringCollector) 176 mr := New(stringReceiver, sc, WithThreadCount(1)) 177 178 m := make(map[string]int) 179 for i, v := range testInput { 180 m[v] = i 181 } 182 183 for n := -1; n <= len(testInput)+1; n++ { 184 sc.a = nil 185 186 for err := range mr.Run(ctx, m, WithThreadCount(n), WithMapperCount(n)) { 187 t.Errorf("%d mappers: %+v", n, err) 188 } 189 190 if n > 0 && len(sc.a) != n { 191 t.Log(n, sc.a) 192 t.Errorf("wrong number of mappers ran, expected %d, but got %d", n, len(sc.a)) 193 } 194 195 var r []rune 196 for _, v := range sc.a { 197 for _, s := range v { 198 r = append(r, []rune(s)...) 199 } 200 } 201 202 sort.Slice(r, func(i, j int) bool { return r[i] < r[j] }) 203 got := string(r) 204 205 if got != testString { 206 t.Logf("mapreduce(map[string]int, %d): %q", n, got) 207 t.Errorf("mapreduce over map with %d mappers did not process all elements, expected %q got %q ", n, testString, got) 208 } 209 } 210 } 211 212 func TestMapReduceOverChannel(t *testing.T) { 213 DefaultThreadCount = -1 214 215 sc := &StringCollector{} 216 mr := New(chanReceiver, sc, WithThreadCount(1)) 217 ctx := context.Background() 218 219 f := func(n int) { 220 sc.a = nil 221 222 ch := make(chan string) 223 224 errch := mr.Run(ctx, ch, WithThreadCount(n), WithMapperCount(n)) 225 226 go func() { 227 defer close(ch) 228 229 for _, s := range testInput { 230 ch <- s 231 } 232 }() 233 234 for err := range errch { 235 t.Errorf("%d mappers: %+v", n, err) 236 } 237 238 if n > 0 && len(sc.a) != n { 239 t.Log(n, sc.a) 240 t.Errorf("wrong number of mappers ran, expected %d, but got %d", n, len(sc.a)) 241 } 242 243 var r []rune 244 for _, v := range sc.a { 245 for _, s := range v { 246 r = append(r, []rune(s)...) 247 } 248 } 249 250 sort.Slice(r, func(i, j int) bool { return r[i] < r[j] }) 251 got := string(r) 252 253 if got != testString { 254 t.Logf("mapreduce(chan string, %d): %q", n, got) 255 t.Errorf("mapreduce over map with %d mappers did not process all elements, expected %q got %q ", n, testString, got) 256 } 257 } 258 259 for i := -1; i <= len(testInput)+1; i++ { 260 f(i) 261 } 262 }