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  }