github.com/go-graphite/carbonapi@v0.17.0/tests/compare/compare.go (about) 1 package compare 2 3 import ( 4 "fmt" 5 "math" 6 "reflect" 7 "testing" 8 9 "github.com/go-graphite/carbonapi/expr/types" 10 ) 11 12 func compareFloat64(v1, v2 float64) bool { 13 if math.IsNaN(v1) && math.IsNaN(v2) { 14 return true 15 } 16 if math.IsInf(v1, 1) && math.IsInf(v2, 1) { 17 return true 18 } 19 20 if math.IsInf(v1, 0) && math.IsInf(v2, 0) { 21 return true 22 } 23 24 d := math.Abs(v1 - v2) 25 return d < eps 26 } 27 28 func deepCompareFields(v1, v2 reflect.Value) bool { 29 if !v1.CanInterface() { 30 return true 31 } 32 t1 := v1.Type() 33 if t1.Comparable() { 34 if t1.Name() == "float64" { 35 return compareFloat64(v1.Interface().(float64), v2.Interface().(float64)) 36 } 37 if t1.Name() == "float32" { 38 v1f64 := float64(v1.Interface().(float32)) 39 v2f64 := float64(v2.Interface().(float32)) 40 return compareFloat64(v1f64, v2f64) 41 } 42 return reflect.DeepEqual(v1.Interface(), v2.Interface()) 43 } else { 44 switch v1.Kind() { 45 case reflect.Struct: 46 if v1.NumField() == 0 { 47 // We don't know how to compare that 48 return false 49 } 50 for i := 0; i < v1.NumField(); i++ { 51 r := deepCompareFields(v1.Field(i), v2.Field(i)) 52 if !r { 53 return r 54 } 55 } 56 case reflect.Slice, reflect.Array: 57 if v1.Len() != v2.Len() { 58 return false 59 } 60 if v1.Len() == 0 { 61 return true 62 } 63 if v1.Index(0).Kind() != v2.Index(0).Kind() { 64 return false 65 } 66 for i := 0; i < v1.Len(); i++ { 67 e1 := v1.Index(i) 68 e2 := v2.Index(i) 69 if !deepCompareFields(e1, e2) { 70 return false 71 } 72 } 73 case reflect.Map: 74 if v1.Len() != v2.Len() { 75 return false 76 } 77 if v1.Len() == 0 { 78 return true 79 } 80 81 keys1 := v1.MapKeys() 82 for _, k := range keys1 { 83 val1 := v1.MapIndex(k) 84 val2 := v2.MapIndex(k) 85 if !deepCompareFields(val1, val2) { 86 return false 87 } 88 } 89 return true 90 case reflect.Func: 91 return v1.Pointer() == v2.Pointer() 92 default: 93 fmt.Printf("unsupported v1.Kind=%v t1.Name=%v, t1.Value=%v\n\n", v1.Kind(), v1.Type().Name(), v1.String()) 94 return false 95 } 96 } 97 return true 98 } 99 100 func MetricDataIsEqual(d1, d2 *types.MetricData, compareTags bool) bool { 101 v1 := reflect.ValueOf(*d1) 102 v2 := reflect.ValueOf(*d2) 103 104 for i := 0; i < v1.NumField(); i++ { 105 if v1.Type().Field(i).Name == "Tags" && !compareTags { 106 continue 107 } 108 r := deepCompareFields(v1.Field(i), v2.Field(i)) 109 if !r { 110 return r 111 } 112 } 113 return true 114 } 115 116 const eps = 0.0000000001 117 118 func NearlyEqual(a, b []float64) bool { 119 if len(a) != len(b) { 120 return false 121 } 122 123 for i, v := range a { 124 // "same" 125 if math.IsNaN(a[i]) && math.IsNaN(b[i]) { 126 continue 127 } 128 if math.IsNaN(a[i]) || math.IsNaN(b[i]) { 129 // unexpected NaN 130 return false 131 } 132 // "close enough" 133 if math.Abs(v-b[i]) > eps { 134 return false 135 } 136 } 137 138 return true 139 } 140 141 func NearlyEqualMetrics(a, b *types.MetricData) bool { 142 if len(a.Values) != len(b.Values) { 143 return false 144 } 145 for i := range a.Values { 146 if (math.IsNaN(a.Values[i]) && !math.IsNaN(b.Values[i])) || (!math.IsNaN(a.Values[i]) && math.IsNaN(b.Values[i])) { 147 return false 148 } 149 // "close enough" 150 if math.Abs(a.Values[i]-b.Values[i]) > eps { 151 return false 152 } 153 } 154 155 return true 156 } 157 158 func MaxInt(a, b int) int { 159 if a >= b { 160 return a 161 } else { 162 return b 163 } 164 } 165 166 func GenerateMetrics(n int, startValue, endValue, stepValue float64) []float64 { 167 values := make([]float64, n) 168 v := startValue 169 for i := 0; i < n; i++ { 170 values[i] = v 171 v += stepValue 172 if v > endValue { 173 v = startValue 174 } 175 } 176 return values 177 } 178 179 func TestMetricData(t *testing.T, got, want []*types.MetricData) { 180 for i := 0; i < MaxInt(len(want), len(got)); i++ { 181 if i >= len(got) { 182 t.Errorf("\n-[%d] = %v", i, want[i]) 183 } else if i >= len(want) { 184 t.Errorf("\n+[%d] = %v", i, got[i]) 185 } else { 186 actual := got[i] 187 if _, ok := actual.Tags["name"]; !ok { 188 t.Errorf("metric %+v with name %v doesn't contain 'name' tag", actual, actual.Name) 189 } 190 if actual == nil { 191 t.Errorf("returned no value") 192 return 193 } 194 if actual.StepTime == 0 { 195 t.Errorf("missing Step for %+v", actual) 196 } 197 if actual.Name != want[i].Name { 198 t.Errorf("bad Name metric[%d]: got %s, want %s", i, actual.Name, want[i].Name) 199 } 200 if !NearlyEqualMetrics(actual, want[i]) { 201 t.Errorf("different values metric[%d] %s: got %v, want %v", i, actual.Name, actual.Values, want[i].Values) 202 } 203 if actual.StepTime != want[i].StepTime { 204 t.Errorf("different StepTime metric[%d] %s: got %v, want %v", i, actual.Name, actual.StepTime, want[i].StepTime) 205 } 206 if actual.StartTime != want[i].StartTime { 207 t.Errorf("different StartTime metric[%d] %s: got %v, want %v", i, actual.Name, actual.StartTime, want[i].StartTime) 208 } 209 if actual.StopTime != want[i].StopTime { 210 t.Errorf("different StopTime metric[%d] %s: got %v, want %v", i, actual.Name, actual.StopTime, want[i].StopTime) 211 } 212 } 213 } 214 } 215 216 // Validate test case values length 217 func TestMetricDataEqLen(t *testing.T, got, want []*types.MetricData) { 218 length := len(want[0].Values) 219 for i := 0; i < MaxInt(len(want), len(got)); i++ { 220 if i >= len(got) { 221 t.Errorf("\n-[%d] = %v", i, want[i]) 222 } else if i >= len(want) { 223 t.Errorf("\n+[%d] = %v", i, got[i]) 224 } else { 225 if length != len(want[i].Values) { 226 t.Fatalf("metric[%d] with name %v contain invalid test case (values length %d not equal with metric[0] values legth %d", i, want[i].Name, length, len(want[i].Values)) 227 } 228 actual := got[i] 229 if _, ok := actual.Tags["name"]; !ok { 230 t.Errorf("metric %+v with name %v doesn't contain 'name' tag", actual, actual.Name) 231 } 232 if actual == nil { 233 t.Errorf("returned no value") 234 return 235 } 236 if actual.StepTime == 0 { 237 t.Errorf("missing Step for %+v", actual) 238 } 239 if actual.Name != want[i].Name { 240 t.Errorf("bad Name metric[%d]: got %s, want %s", i, actual.Name, want[i].Name) 241 } 242 if !NearlyEqualMetrics(actual, want[i]) { 243 t.Errorf("different values metric[%d] %s: got %v, want %v", i, actual.Name, actual.Values, want[i].Values) 244 } 245 if actual.StepTime != want[i].StepTime { 246 t.Errorf("different StepTime metric[%d] %s: got %v, want %v", i, actual.Name, actual.StepTime, want[i].StepTime) 247 } 248 if actual.StartTime != want[i].StartTime { 249 t.Errorf("different StartTime metric[%d] %s: got %v, want %v", i, actual.Name, actual.StartTime, want[i].StartTime) 250 } 251 if actual.StopTime != want[i].StopTime { 252 t.Errorf("different StopTime metric[%d] %s: got %v, want %v", i, actual.Name, actual.StopTime, want[i].StopTime) 253 } 254 } 255 } 256 }