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 }