github.com/go-graphite/carbonapi@v0.17.0/expr/helper/helper_test.go (about) 1 package helper 2 3 import ( 4 "fmt" 5 "math" 6 "reflect" 7 "testing" 8 9 "github.com/go-graphite/carbonapi/expr/types" 10 ) 11 12 func TestGCD(t *testing.T) { 13 tests := []struct { 14 arg1 int64 15 arg2 int64 16 expected int64 17 }{ 18 { 19 13, 20 17, 21 1, 22 }, 23 { 24 14, 25 21, 26 7, 27 }, 28 { 29 12, 30 16, 31 4, 32 }, 33 } 34 for _, tt := range tests { 35 t.Run(fmt.Sprintf("GDC(%v, %v)=>%v", tt.arg1, tt.arg2, tt.expected), func(t *testing.T) { 36 value := GCD(tt.arg1, tt.arg2) 37 if value != tt.expected { 38 t.Errorf("GCD of %v and %v != %v: %v", tt.arg1, tt.arg2, tt.expected, value) 39 } 40 }) 41 } 42 } 43 44 func TestLCM(t *testing.T) { 45 tests := []struct { 46 args []int64 47 expected int64 48 }{ 49 { 50 []int64{2, 3}, 51 6, 52 }, 53 { 54 []int64{}, 55 0, 56 }, 57 { 58 []int64{15}, 59 15, 60 }, 61 { 62 []int64{10, 15, 20}, 63 60, 64 }, 65 } 66 for _, tt := range tests { 67 t.Run(fmt.Sprintf("LMC(%v)=>%v", tt.args, tt.expected), func(t *testing.T) { 68 value := LCM(tt.args...) 69 if value != tt.expected { 70 t.Errorf("LCM of %v != %v: %v", tt.args, tt.expected, value) 71 } 72 }) 73 } 74 } 75 76 func TestGetCommonStep(t *testing.T) { 77 tests := []struct { 78 metrics []*types.MetricData 79 commonStep int64 80 changed bool 81 }{ 82 // Different steps and start/stop time 83 { 84 []*types.MetricData{ 85 types.MakeMetricData("metric1", make([]float64, 15), 5, 5), // 5..80 86 types.MakeMetricData("metric2", make([]float64, 30), 2, 4), // 4..64 87 types.MakeMetricData("metric2", make([]float64, 25), 3, 6), // 6..81 88 }, 89 30, 90 true, 91 }, 92 // Same set of points 93 { 94 []*types.MetricData{ 95 types.MakeMetricData("metric1", make([]float64, 15), 5, 5), // 5..80 96 types.MakeMetricData("metric2", make([]float64, 15), 5, 5), // 5..80 97 types.MakeMetricData("metric3", make([]float64, 15), 5, 5), // 5..80 98 }, 99 5, 100 false, 101 }, 102 // Same step, different lengths 103 { 104 []*types.MetricData{ 105 types.MakeMetricData("metric1", make([]float64, 5), 5, 15), // 15..40 106 types.MakeMetricData("metric2", make([]float64, 8), 5, 30), // 30..70 107 types.MakeMetricData("metric3", make([]float64, 4), 5, 35), // 35..55 108 }, 109 5, 110 false, 111 }, 112 } 113 for i, tt := range tests { 114 t.Run(fmt.Sprintf("Set %v", i), func(t *testing.T) { 115 com, changed := GetCommonStep(tt.metrics) 116 if com != tt.commonStep { 117 t.Errorf("Result of GetCommonStep: %v; expected is %v", com, tt.commonStep) 118 } 119 if changed != tt.changed { 120 t.Errorf("GetCommonStep changed: %v; expected is %v", changed, tt.changed) 121 } 122 }) 123 } 124 } 125 126 func TestScaleToCommonStep(t *testing.T) { 127 NaN := math.NaN() 128 tests := []struct { 129 name string 130 metrics []*types.MetricData 131 commonStep int64 132 expected []*types.MetricData 133 }{ 134 { 135 "Normal metrics", 136 []*types.MetricData{ 137 types.MakeMetricData("metric1", []float64{1, 3, 5, 7, 9, 11, 13, 15, 17}, 1, 4), // 4..13 138 types.MakeMetricData("metric2", []float64{1, 2, 3, 4, 5}, 2, 4), // 4..14 139 types.MakeMetricData("metric3", []float64{1, 2, 3, 4, 5, 6}, 3, 3), // 3..21 140 }, 141 0, 142 []*types.MetricData{ 143 types.MakeMetricData("metric1", []float64{2, 10, 17, NaN}, 6, 0), // 0..18 144 types.MakeMetricData("metric2", []float64{1, 3, 5, NaN}, 6, 0), // 0..18 145 types.MakeMetricData("metric3", []float64{1, 2.5, 4.5, 6}, 6, 0), // 0..24 146 }, 147 }, 148 149 // Indx | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 150 // commonStep 6 151 // Start 0 (2 - 2 % 6) 152 // 153 // ConsolidationFunc = "sum", XFilesFactor = 0.45 154 // metric1 | | | | N | 3 | 5 | 7 | 9 | 11 | 13 | 15 | 17 | | | | | | | | | | | | | | | 155 // metric1 | N | | | | | | 72 | | | | | | | | | | | | | | | | | | | | 156 // 157 // ConsolidationFunc = "min", XFilesFactor = 0.45 158 // metric2 | | | | | 1 | | 2 | | 3 | | 4 | | 5 | | | | | | | | | | | | | | 159 // metric2 | N | | | | | | 2 | | | | | | N | | | | | | | | | | | | | | 160 { 161 "xFilesFactor and custom aggregate function", 162 []*types.MetricData{ 163 types.MakeMetricData("metric1", []float64{NaN, 3, 5, 7, 9, 11, 13, 15, 17}, 1, 3).SetConsolidationFunc("sum").SetXFilesFactor(0.45), 164 types.MakeMetricData("metric2", []float64{1, 2, 3, 4, 5}, 2, 4).SetConsolidationFunc("min").SetXFilesFactor(0.45), 165 types.MakeMetricData("metric3", []float64{1, 2, 3, 4, 5, 6}, 3, 3).SetConsolidationFunc("max").SetXFilesFactor(0.51), 166 types.MakeMetricData("metric6", []float64{1, 2, 3, 4, 5}, 6, 0), 167 }, 168 0, 169 []*types.MetricData{ 170 types.MakeMetricData("metric1", []float64{NaN, 72, NaN, NaN, NaN}, 6, 0), // 0..12 171 types.MakeMetricData("metric2", []float64{NaN, 2, NaN, NaN, NaN}, 6, 0), // 0..18 172 types.MakeMetricData("metric3", []float64{NaN, 3, 5, NaN, NaN}, 6, 0), // 0..24 173 types.MakeMetricData("metric6", []float64{1, 2, 3, 4, 5}, 6, 0), // 0..30, unchanged 174 }, 175 }, 176 { 177 "Custom common step", 178 []*types.MetricData{ 179 types.MakeMetricData("metric1", []float64{NaN, 3, 5, 7, 9, 11, 13, 15, 17}, 1, 3), // 3..12 180 types.MakeMetricData("metric2", []float64{1, 2, 3, 4, 5}, 2, 4), // 4..14 181 types.MakeMetricData("metric3", []float64{1, 2, 3, 4, 5, 6}, 3, 3), // 3..21 182 types.MakeMetricData("metric6", []float64{1, 2, 3, 4, 5}, 6, 0), // 0..30 183 }, 184 12, 185 []*types.MetricData{ 186 types.MakeMetricData("metric1", []float64{10, NaN, NaN}, 12, 0), // 0..12 187 types.MakeMetricData("metric2", []float64{2.5, 5, NaN}, 12, 0), // 0..18 188 types.MakeMetricData("metric3", []float64{2, 5, NaN}, 12, 0), // 0..24 189 types.MakeMetricData("metric6", []float64{1.5, 3.5, 5}, 12, 0), // 0..30, unchanged 190 }, 191 }, 192 } 193 for _, tt := range tests { 194 t.Run(tt.name, func(t *testing.T) { 195 result := ScaleToCommonStep(tt.metrics, tt.commonStep) 196 if len(result) != len(tt.expected) { 197 t.Errorf("Result has different length %v than expected %v", len(result), len(tt.expected)) 198 } 199 for i, r := range result { 200 e := tt.expected[i] 201 if len(r.Values) != len(e.Values) { 202 t.Fatalf("Values of result[%v] has the different length %+v than expected %+v", i, r.Values, e.Values) 203 } 204 for v, rv := range r.Values { 205 ev := e.Values[v] 206 if math.IsNaN(rv) != math.IsNaN(ev) { 207 t.Errorf("One of result[%v][%v] is NaN, but not the second: result=%v, expected=%v", i, v, rv, ev) 208 } else if !math.IsNaN(rv) && (rv != ev) { 209 t.Errorf("result[%v][%v] %v != expected[%v][%v]: %v", i, v, rv, i, v, ev) 210 } 211 } 212 if r.StartTime != e.StartTime { 213 t.Errorf("result[%v].StartTime %v != expected[%v].StartTime %v", i, r.StartTime, i, e.StartTime) 214 } 215 if r.StopTime != e.StopTime { 216 t.Errorf("result[%v].StopTime %v != expected[%v].StopTime %v", i, r.StopTime, i, e.StopTime) 217 } 218 if r.StepTime != e.StepTime { 219 t.Errorf("result[%v].StepTime %v != expected[%v].StepTime %v", i, r.StepTime, i, e.StepTime) 220 } 221 } 222 }) 223 } 224 } 225 226 func TestGetCommonTags(t *testing.T) { 227 first := map[string]string{"tag1": "value1", "tag2": "onevalue", "tag3": "value3"} 228 second := map[string]string{"tag1": "value1", "tag2": "differentvalue", "tag4": "value4"} 229 230 expected := map[string]string{"tag1": "value1"} 231 result := GetCommonTags([]*types.MetricData{{Tags: first}, {Tags: second}}) 232 233 if !reflect.DeepEqual(expected, result) { 234 t.Errorf("expected %v, got %v", expected, result) 235 } 236 }