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  }