github.com/grailbio/bigslice@v0.0.0-20230519005545-30c4c12152ad/example/max_test.go (about)

     1  // Copyright 2020 GRAIL, Inc. All rights reserved.
     2  // Use of this source code is governed by the Apache 2.0
     3  // license that can be found in the LICENSE file.
     4  
     5  package example
     6  
     7  import (
     8  	"context"
     9  	"math/rand"
    10  	"reflect"
    11  	"testing"
    12  	"testing/quick"
    13  
    14  	"github.com/grailbio/bigslice"
    15  	"github.com/grailbio/bigslice/slicetest"
    16  )
    17  
    18  // TestIntMax verifies that IntMax works on a trivial input. Its main purpose
    19  // is to illustrate the basic test flow and usage of slicetest.
    20  func TestIntMax(t *testing.T) {
    21  	slice := bigslice.Const(2,
    22  		[]int{0, 0, 0, 1, 1, 2},
    23  		[]int{6, 5, 4, 3, 2, 1},
    24  	)
    25  	slice = IntMax(slice)
    26  	scanner := slicetest.Run(t, slice)
    27  	var (
    28  		key int
    29  		val int
    30  		got = make(map[int]int)
    31  	)
    32  	for scanner.Scan(context.Background(), &key, &val) {
    33  		got[key] = val
    34  	}
    35  	if err := scanner.Err(); err != nil {
    36  		t.Fatal(err)
    37  	}
    38  	want := map[int]int{
    39  		0: 6,
    40  		1: 3,
    41  		2: 1,
    42  	}
    43  	if !reflect.DeepEqual(got, want) {
    44  		t.Errorf("got %v, want %v", got, want)
    45  	}
    46  }
    47  
    48  // intMaxTestCase is a test case for our property test. Random cases will be
    49  // generated by testing.quick.
    50  type intMaxTestCase struct {
    51  	numShards int
    52  	valsByKey map[int][]int
    53  }
    54  
    55  // Generate implements the quick.Generator interface.
    56  func (intMaxTestCase) Generate(r *rand.Rand, size int) reflect.Value {
    57  	valsByKey := make(map[int][]int)
    58  	for i := 0; i < size; i++ {
    59  		for j := 0; j < r.Intn(size); j++ {
    60  			valsByKey[i] = append(valsByKey[i], r.Int())
    61  		}
    62  	}
    63  	return reflect.ValueOf(intMaxTestCase{
    64  		numShards: r.Intn(size) + 1, // At least 1 shard.
    65  		valsByKey: valsByKey,
    66  	})
    67  }
    68  
    69  // TestIntMaxProperties is a more sophisticated property test of IntMax. It
    70  // illustrates the integration of property-based testing, which is likely useful
    71  // for asserting invariants of large-scale processing, with slice testing
    72  // machinery.
    73  func TestIntMaxProperties(t *testing.T) {
    74  	f := func(c intMaxTestCase) bool {
    75  		var (
    76  			ks []int
    77  			vs []int
    78  		)
    79  		for k, kVals := range c.valsByKey {
    80  			for _, v := range kVals {
    81  				ks = append(ks, k)
    82  				vs = append(vs, v)
    83  			}
    84  		}
    85  		slice := bigslice.Const(c.numShards, ks, vs)
    86  		slice = IntMax(slice)
    87  		scanner := slicetest.Run(t, slice)
    88  		var (
    89  			k   int
    90  			max int
    91  			got = make(map[int]int)
    92  		)
    93  		for scanner.Scan(context.Background(), &k, &max) {
    94  			// Each key exists in the input.
    95  			inVs, ok := c.valsByKey[k]
    96  			if !ok {
    97  				t.Logf("key not in input: %v", k)
    98  				return false
    99  			}
   100  			// The maximum value exists in the input for the key.
   101  			var found bool
   102  			for _, inV := range inVs {
   103  				if max == inV {
   104  					found = true
   105  				}
   106  			}
   107  			if !found {
   108  				t.Logf("value not found key inputs for key %v: %v", k, max)
   109  				return false
   110  			}
   111  			// No input for the key is greater than the computed maximum.
   112  			for _, inV := range inVs {
   113  				if max < inV {
   114  					return false
   115  				}
   116  			}
   117  			// No key is duplicated.
   118  			if _, ok = got[k]; ok {
   119  				t.Logf("duplicate k: %v", k)
   120  				return false
   121  			}
   122  			got[k] = max
   123  		}
   124  
   125  		return true
   126  	}
   127  	c := quick.Config{MaxCount: 10}
   128  	if err := quick.Check(f, &c); err != nil {
   129  		t.Error(err)
   130  	}
   131  }