github.com/grailbio/bigslice@v0.0.0-20230519005545-30c4c12152ad/cmd/slicetrace/quartile_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 main 6 7 import ( 8 "sort" 9 "testing" 10 "time" 11 12 fuzz "github.com/google/gofuzz" 13 ) 14 15 // TestComputeQuartiles verifies that quartiles are computed correctly for 16 // select test cases. 17 func TestComputeQuartiles(t *testing.T) { 18 for _, c := range []struct { 19 name string 20 ds []int64 21 q1, q2, q3 int64 22 }{ 23 { 24 name: "OneElement", 25 ds: []int64{0}, 26 q1: 0, 27 q2: 0, 28 q3: 0, 29 }, 30 { 31 name: "TwoElementAllSame", 32 ds: []int64{0, 0}, 33 q1: 0, 34 q2: 0, 35 q3: 0, 36 }, 37 { 38 name: "TwoElement", 39 ds: []int64{0, 100}, 40 q1: 0, 41 q2: 50, 42 q3: 100, 43 }, 44 { 45 name: "ThreeElementAllSame", 46 ds: []int64{0, 0, 0}, 47 q1: 0, 48 q2: 0, 49 q3: 0, 50 }, 51 { 52 name: "ThreeElementLowSame", 53 ds: []int64{0, 0, 200}, 54 q1: 0, 55 q2: 0, 56 q3: 100, 57 }, 58 { 59 name: "ThreeElementHighSame", 60 ds: []int64{0, 200, 200}, 61 q1: 100, 62 q2: 200, 63 q3: 200, 64 }, 65 { 66 name: "ThreeElement", 67 ds: []int64{0, 100, 200}, 68 q1: 50, 69 q2: 100, 70 q3: 150, 71 }, 72 { 73 name: "FourElementAllSame", 74 ds: []int64{0, 0, 0, 0}, 75 q1: 0, 76 q2: 0, 77 q3: 0, 78 }, 79 { 80 name: "FourElementSomeSame", 81 ds: []int64{0, 100, 100, 200}, 82 q1: 50, 83 q2: 100, 84 q3: 150, 85 }, 86 { 87 name: "FourElement", 88 ds: []int64{0, 100, 200, 300}, 89 q1: 50, 90 q2: 150, 91 q3: 250, 92 }, 93 { 94 name: "FiveElementAllSame", 95 ds: []int64{0, 0, 0, 0, 0}, 96 q1: 0, 97 q2: 0, 98 q3: 0, 99 }, 100 { 101 name: "FiveElementSomeSame", 102 ds: []int64{0, 100, 100, 100, 200}, 103 q1: 100, 104 q2: 100, 105 q3: 100, 106 }, 107 { 108 name: "FiveElement", 109 ds: []int64{0, 100, 200, 300, 400}, 110 q1: 100, 111 q2: 200, 112 q3: 300, 113 }, 114 } { 115 t.Run(c.name, func(t *testing.T) { 116 ds := make([]time.Duration, len(c.ds)) 117 for i := range ds { 118 ds[i] = time.Duration(c.ds[i]) 119 } 120 q1, q2, q3 := computeQuartiles(ds) 121 if got, want := q1, time.Duration(c.q1); got != want { 122 t.Errorf("got %v, want %v", got, want) 123 } 124 if got, want := q2, time.Duration(c.q2); got != want { 125 t.Errorf("got %v, want %v", got, want) 126 } 127 if got, want := q3, time.Duration(c.q3); got != want { 128 t.Errorf("got %v, want %v", got, want) 129 } 130 }) 131 } 132 } 133 134 // TestComputeQuartilesFuzz verifies that the quartiles computed from fuzzed 135 // values are within [min, max] and monotonically increasing. 136 func TestComputeQuartilesFuzz(t *testing.T) { 137 const N = 10000 138 f := fuzz.New() 139 for i := 0; i < N; i++ { 140 var ds []time.Duration 141 f.Fuzz(&ds) 142 if len(ds) == 0 { 143 // computeQuartiles does not handle empty slices. 144 continue 145 } 146 sort.Slice(ds, func(i, j int) bool { 147 return ds[i] < ds[j] 148 }) 149 var ( 150 min = time.Duration(1<<63 - 1) 151 max = time.Duration(-1 << 63) 152 ) 153 for _, d := range ds { 154 if d < min { 155 min = d 156 } 157 if max < d { 158 max = d 159 } 160 } 161 q1, q2, q3 := computeQuartiles(ds) 162 t.Logf("test durations: %v", ds) 163 if q1 < min { 164 t.Errorf("%v(q1) < %v(min)", q1, min) 165 } 166 if q2 < q1 { 167 t.Errorf("%v(q2) < %v(q1)", q2, q1) 168 } 169 if q3 < q2 { 170 t.Errorf("%v(q3) < %v(q2)", q3, q2) 171 } 172 if max < q3 { 173 t.Errorf("%v(max) < %v(q3)", max, q3) 174 } 175 } 176 }